
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 perl
variante:
$ 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_in
e, como estamos usando o gawk de qualquer maneira, algumas outras extensões convenientes, mas desnecessárias, podemos aplicar oIdioma Decorar-Classificar-Undecorarcolocando 1
na frente de qualquer caractere minúsculo e 2
na 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 getopts
normalmente para analisar as rap
opçõ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, . ./file
assumindo que a rap
função havia sido declarada anteriormente.
Para analisar as opções na rap
funçã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 while
loop e agir sobre elas após o loop, evitamos agir várias vezes em opções duplicadas.