
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 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 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_in
y, dado que de todos modos estamos usando gawk, algunas otras extensiones convenientes pero innecesarias, podemos aplicar elModismo decorar-ordenar-desdecorarcolocando 1
delante de los caracteres en minúscula y 2
delante 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 getopts
como de costumbre para analizar las rap
opciones 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 . ./file
que la rap
función había sido declarada previamente.
Para analizar las opciones en la rap
funció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 while
bucle y actuar sobre ellas después del bucle, evitamos actuar sobre opciones duplicadas varias veces.