É possível ordenar as letras de uma palavra em uma linha de texto?

É possível ordenar as letras de uma palavra em uma linha de texto?

Portanto, tenho um arquivo cheio de comandos de teste que gosto de executar em algumas de minhas funções para ter certeza de que estão lidando corretamente com todas as situações possíveis. Não faz sentido ter comandos duplicados. Aqui estão alguns exemplos:

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

... minha função 'rap' usa uma vírgula em vez de um travessão para indicar o início das opções de letras, então há alguns argumentos a seguir. Como a ordem dessas opções não importa:

rap ,Bf X11
rap ,fB X11

... são exatamente o mesmo comando. Fácil de remover linhas duplicadas do arquivo, é claro, no entanto, para evitar o problema acima, o que eu gostaria de fazer é classificar as opções em ordem alfabética para que o acima ficasse:

rap ,Bf X11
rap ,Bf X11

... e eu poderia excluir as duplicatas. Algo assim pode ser feito sem heroísmo? Observe que isso não está classificando 'por' a lista de opções, mas classificando as próprias opções.

Responder1

Outra 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 sua exigência extra de ter letras minúsculas antes das maiúsculas, você pode confiar no fato de que em ASCII 'x'é 'X' ^ 32(e 'X'é '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

Responder2

Você poderia usar perl para capturar uma sequência de caracteres de palavras após uma vírgula, dividir o resultado em uma matriz, classificá-la e substituir o 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

Conforme solicitado, aqui está uma maneira (provavelmente abaixo do ideal) de classificar todas as opções de letras minúsculas antes de todas as maiú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

Responder3

Usando GNU awk parasorted_ine, como estamos usando o gawk de qualquer maneira, algumas outras extensões convenientes, mas desnecessárias, podemos aplicar oIdioma Decorar-Classificar-Undecorarcolocando 1na frente de qualquer caractere minúsculo e 2na frente do superior para forçar os caracteres minúsculos a serem classificados antes dos maiúsculos e, em seguida, remover essas decorações novamente 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

Responder4

Se substituirmos a vírgula no arquivo de entrada por um travessão, podemos usar getoptsnormalmente para analisar as rapopções da função.

Essa mudança pode ser feita com sed, e supondo que só precisamos mudar rap ,no início de qualquer linha para rap -, ficaria assim:

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

Poderíamos então simplesmente fornecer o arquivo gerado em nosso script, . ./fileassumindo que a rapfunção havia sido declarada anteriormente.

Para analisar as opções na rapfunção:

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'
}

Observe que, ao definir as variáveis ​​de flag no whileloop e agir sobre elas após o loop, evitamos agir várias vezes em opções duplicadas.

informação relacionada