¿Es posible ordenar las letras de una palabra en una línea de texto?

¿Es posible ordenar las letras de una palabra en una línea de texto?

Entonces tengo un archivo lleno de comandos de prueba que me gusta ejecutar en algunas de mis funciones para asegurarme de que manejan todas las situaciones posibles correctamente. Aunque no tiene sentido tener comandos duplicados. A continuación se muestran algunos ejemplos:

rap ,Xflg MIT X11           
rap ,XPBfl 'MITER'
rap ,Bflg share git-grep    
rap ,bfl X11
rap ,Bfl xzfgrep
rap ,Bf X11

... mi función 'rap' usa una coma en lugar de un guión para indicar el inicio de las opciones de letra, luego hay algún argumento a continuación. Dado que el orden de estas opciones no importa:

rap ,Bf X11
rap ,fB X11

... son exactamente el mismo comando. Por supuesto, es fácil eliminar líneas duplicadas del archivo; sin embargo, para evitar el problema anterior, lo que me gustaría poder hacer es ordenar las opciones alfabéticamente para que termine lo anterior:

rap ,Bf X11
rap ,Bf X11

... y luego podría eliminar los duplicados. ¿Se puede hacer algo así sin actos heroicos? Tenga en cuenta que esto no es ordenar "por" la lista de opciones, sino ordenar las opciones en sí.

Respuesta1

Otra perlvariante:

$ perl -pe 's{^rap ,\K\S+}{join "", sort split //, $&}e' file
rap ,Xfgl MIT X11
rap ,BPXfl 'MITER'
rap ,Bfgl share git-grep
rap ,bfl X11
rap ,Bfl xzfgrep
rap ,Bf X11

Para su requisito adicional de tener letras minúsculas antes que mayúsculas, puede confiar en el hecho de que en ASCII, 'x'es 'X' ^ 32(y 'X'es 'x' ^ 32):

$ perl -pe 's{^rap ,\K\S+}{join "", sort {(ord($a)^32) <=> (ord($b)^32)} split //, $&}e' file
rap ,fglX MIT X11
rap ,flBPX 'MITER'
rap ,fglB share git-grep
rap ,bfl X11
rap ,flB xzfgrep
rap ,fB X11

Respuesta2

Puede usar Perl para capturar una secuencia de caracteres de palabras después de una coma, dividir el resultado en una matriz, ordenarlo y sustituir el resultado:

$ perl -pe 's{(?<=,)(\w+)}{join "", sort split(//, $1)}e' yourfile 
rap ,Xfgl MIT X11           
rap ,BPXfl 'MITER'
rap ,Bfgl share git-grep    
rap ,bfl X11
rap ,Bfl xzfgrep
rap ,Bf X11

Según lo solicitado, aquí hay una forma (probablemente subóptima) de ordenar todas las opciones de letras minúsculas antes que todas las mayúsculas:

$ perl -pe 's{(?<=,)(\w+)}{@opts = split(//,$1); join "", 
    (sort grep /[[:lower:]]/,@opts), (sort grep /[^[:lower:]]/, @opts)
  }e' yourfile 
rap ,fglX MIT X11           
rap ,flBPX 'MITER'
rap ,fglB share git-grep    
rap ,bfl X11
rap ,flB xzfgrep
rap ,fB X11

Respuesta3

Usando GNU awk parasorted_iny, dado que de todos modos estamos usando gawk, algunas otras extensiones convenientes pero innecesarias, podemos aplicar elModismo decorar-ordenar-desdecorarcolocando 1delante de los caracteres en minúscula y 2delante de los caracteres en mayúscula para forzar que los caracteres en minúscula se ordenen antes que los mayúsculas y luego elimine esas decoraciones nuevamente antes de imprimir:

$ cat tst.awk
BEGIN { PROCINFO["sorted_in"] = "@val_str_asc" }
match( $0, /^(\s*\S+\s*,)(\S+)(.*)/, a ) {
    gsub( /[[:lower:]]/, "1 &,", a[2] )        # Decorate
    gsub( /[[:upper:]]/, "2 &,", a[2] )

    sorted = ""
    split(a[2],opts,",")
    for ( idx in opts ) {                      # Sort
        sorted = sorted opts[idx]
    }

    gsub( /[[:digit:] ,]/, "", sorted )        # Undecorate
    $0 = a[1] sorted a[3]
}
{ print }

$ awk -f tst.awk file
rap ,fglX MIT X11
rap ,flBPX 'MITER'
rap ,fglB share git-grep
rap ,bfl X11
rap ,flB xzfgrep
rap ,fB X11

Respuesta4

Si reemplazamos la coma en el archivo de entrada con un guión, podemos usarla getoptscomo de costumbre para analizar las rapopciones de la función.

Ese cambio se puede hacer con sed, y suponiendo que solo necesitemos cambiar rap ,al comienzo de cualquier línea a rap -, se vería así:

sed 's/^rap ,/rap -/' file.in >file

Entonces podríamos simplemente obtener el archivo generado en nuestro script asumiendo . ./fileque la rapfunción había sido declarada previamente.

Para analizar las opciones en la rapfunción:

rap () {
        OPTIND=1

        unset -v B_flag P_flag X_flag
        unset -v b_flag f_flag g_flag l_flag

        while getopts BPXbfgl opt; do
                case $opt in
                        B) B_flag=true ;;
                        P) P_flag=true ;;
                        X) X_flag=true ;;
                        b) b_flag=true ;;
                        f) f_flag=true ;;
                        g) g_flag=true ;;
                        l) l_flag=true ;;
                        *) echo 'Error' >&2; return 1
                esac
        done
        shift "$(( OPTIND - 1 ))"

        # Act on set flags here.

        if "${f_flag-false}"; then
                echo 'The -f option was used'
        fi

        # The non-options are available in "$@".

        printf 'Other argument: %s\n' "$@"
        printf -- '---\n'
}

Tenga en cuenta que al configurar las variables de bandera en el whilebucle y actuar sobre ellas después del bucle, evitamos actuar sobre opciones duplicadas varias veces.

información relacionada