
Ich habe also eine Datei voller Testbefehle, die ich gerne für einige meiner Funktionen ausführe, um sicherzustellen, dass sie alle möglichen Situationen richtig handhaben. Es macht jedoch keinen Sinn, doppelte Befehle zu haben. Hier sind einige Beispiele:
rap ,Xflg MIT X11
rap ,XPBfl 'MITER'
rap ,Bflg share git-grep
rap ,bfl X11
rap ,Bfl xzfgrep
rap ,Bf X11
... meine Funktion 'rap' verwendet ein Komma statt eines Bindestrichs, um den Beginn von Buchstabenoptionen anzuzeigen, dann folgt ein Argument. Da die Reihenfolge dieser Optionen keine Rolle spielt:
rap ,Bf X11
rap ,fB X11
... sind genau derselbe Befehl. Natürlich ist es einfach, doppelte Zeilen aus der Datei zu entfernen. Um das obige Problem zu vermeiden, würde ich die Optionen gerne alphabetisch sortieren, sodass das obige Ergebnis lautet:
rap ,Bf X11
rap ,Bf X11
... und dann könnte ich die Duplikate löschen. Kann so etwas ohne Heldentaten gemacht werden? Beachten Sie, dass dies nicht das Sortieren „nach“ der Liste der Optionen ist, sondern das Sortieren der Optionen selbst.
Antwort1
Eine andere 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
Für Ihre zusätzliche Anforderung, dass Kleinbuchstaben vor Großbuchstaben stehen müssen, können Sie sich auf die Tatsache verlassen, dass in ASCII 'x'
gilt 'X' ^ 32
(und 'X'
ist '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
Antwort2
Sie könnten Perl verwenden, um eine Folge von Wortzeichen nach einem Komma zu erfassen, das Ergebnis in ein Array aufzuteilen, dieses zu sortieren und das Ergebnis zu ersetzen:
$ 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
Wie gewünscht gibt es hier eine (wahrscheinlich nicht optimale) Möglichkeit, alle Kleinbuchstaben vor allen Großbuchstaben zu sortieren:
$ 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
Antwort3
Verwendung von GNU awk fürsorted_in
und da wir gawk sowieso verwenden, ein paar andere praktische, aber unnötige Erweiterungen, können wir dieDekorieren-Sortieren-Entdekorieren - Redewendungindem Sie es 1
vor alle Kleinbuchstaben und 2
vor alle Großbuchstaben setzen, um zu erzwingen, dass alle Kleinbuchstaben vor den Großbuchstaben sortiert werden, und diese Verzierungen dann vor dem Drucken wieder entfernen:
$ 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
Antwort4
Wenn wir das Komma in der Eingabedatei durch einen Bindestrich ersetzen, können wir die Optionen der Funktion getopts
wie gewohnt analysieren .rap
Diese Änderung kann mit durchgeführt werden . Unter der Annahme, dass wir immer nur am Anfang einer Zeile in sed
ändern müssen , sähe das folgendermaßen aus:rap ,
rap -
sed 's/^rap ,/rap -/' file.in >file
Wir könnten dann einfach die generierte Datei in unserem Skript als Quelle verwenden, . ./file
vorausgesetzt, die rap
Funktion wurde zuvor deklariert.
So analysieren Sie die Optionen in der rap
Funktion:
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'
}
Beachten Sie, dass wir durch das Festlegen der Flagvariablen in der while
Schleife und das Bearbeiten dieser nach der Schleife vermeiden, dass doppelte Optionen mehrfach bearbeitet werden.