lcomma() { sed '
$x;$G;/\(.*\),/!H;//!{$!d
}; $!x;$s//\1/;s/^\n//'
}
Das sollte nur das letzte Vorkommen von a ,in einer Eingabedatei entfernen - und es werden weiterhin diejenigen gedruckt, in denen a ,nicht vorkommt. Grundsätzlich werden Zeilenfolgen gepuffert, die kein Komma enthalten.
Wenn es auf ein Komma stößt, tauscht es den aktuellen Zeilenpuffer mit dem Haltepuffer aus und gibt auf diese Weise gleichzeitig alle Zeilen aus, die seit dem letzten Komma aufgetreten sind, und gibt seinen Haltepuffer frei.
Ich habe gerade meine Verlaufsdatei durchsucht und Folgendes gefunden:
lmatch(){ set "USAGE:\
lmatch /BRE [-(((s|-sub) BRE)|(r|-ref)) REPL [-(f|-flag) FLAG]*]*
" "${1%"${1#?}"}" "$@"
eval "${ZSH_VERSION:+emulate sh}"; eval '
sed " 1x; \\$3$2!{1!H;\$!d
}; \\$3$2{x;1!p;\$!d;x
}; \\$3$2!x;\\$3$2!b'"
$( unset h;i=3 p=:-:shfr e='\033[' m=$(($#+1)) f=OPTERR
[ -t 2 ] && f=$e\2K$e'1;41;17m}\r${h-'$f$e\0m
f='\${$m?"\"${h-'$f':\t\${$i$e\n}\$1\""}\\c' e=} _o=
o(){ IFS=\ ;getopts $p a "$1" &&
[ -n "${a#[?:]}" ] &&
o=${a#-}${OPTARG-${1#-?}} ||
! eval "o=$f;o=\${o%%*\{$m\}*}"
}; a(){ case ${a#[!-]}$o in (?|-*) a=;;esac; o=
set $* "${3-$2$}{$((i+=!${#a}))${a:+#-?}}"\
${3+$2 "{$((i+=1))$e"} $2
IFS=$; _o=${_o%"${3+$_o} "*}$*\
}; while eval "o \"\${$((i+=(OPTIND=1)))}\""
do case ${o#[!$a]} in
(s*|ub) a s 2 '' ;;
(r*|ef) a s 2 ;;
(f*|lag) a ;;
(h*|elp) h= o; break ;;
esac; done; set -f; printf "\t%b\n\t" $o $_o
)\"";}
Es ist eigentlich ziemlich gut. Ja, es verwendet eval, aber es übergibt ihm nie etwas anderes als einen numerischen Verweis auf seine Argumente. Es werden beliebige sedSkripte für die Behandlung eines letzten Matches erstellt. Ich werde Ihnen zeigen:
printf "%d\" %d' %d\" %d'\n" $(seq 5 5 200) |
tee /dev/fd/2 |
lmatch d^.0 \ #all re's delimit w/ d now
-r '&&&&' \ #-r or --ref like: '...s//$ref/...'
--sub \' sq \ #-s or --sub like: '...s/$arg1/$arg2/...'
--flag 4 \ #-f or --flag appended to last -r or -s
-s\" \\dq \ #short opts can be '-s $arg1 $arg2' or '-r$arg1'
-fg #tacked on so: '...s/"/dq/g...'
Das druckt folgendes zu stderr. Dies ist eine Kopie der lmatchEingabe von:
5" 10' 15" 20'
25" 30' 35" 40'
45" 50' 55" 60'
65" 70' 75" 80'
85" 90' 95" 100'
105" 110' 115" 120'
125" 130' 135" 140'
145" 150' 155" 160'
165" 170' 175" 180'
185" 190' 195" 200'
Die evaled-Subshell der Funktion durchläuft alle Argumente einmal. Wenn es über sie läuft, iteriert es einen Zähler entsprechend dem Kontext für jeden Schalter und überspringt so viele Argumente für die nächste Iteration. Von da an macht es eines von ein paar Dingen pro Argument:
- Für jede Option fügt die Option Parser
$azu $o. $awird basierend auf dem Wert zugewiesen, der $ifür jedes verarbeitete Argument um die Anzahl der Argumente erhöht wird. $aerhält einen der beiden folgenden Werte:
a=$((i+=1)) - Dies wird zugewiesen, wenn entweder an eine Short-Option kein Argument angehängt ist oder wenn die Option eine Long-Option war.
a=$i#-?- dies wird zugewiesen , wenn die Option ein kurzer und nicht hat seine arg angehängt.
a=\${$a}${1:+$d\${$(($1))\}}- Unabhängig von der anfänglichen Zuweisung wird $ader Wert immer in geschweifte Klammern gesetzt und in einigen -sFällen $ium eins erhöht und zusätzlich ein abgegrenztes Feld angehängt.
Das Ergebnis ist, dass evalniemals ein String übergeben wird, der Unbekanntes enthält. Jedes der Befehlszeilenargumente wird durch seine numerische Argumentnummer bezeichnet - selbst das Trennzeichen, das aus dem ersten Zeichen des ersten Arguments extrahiert wird und das einzige Mal ist, dass Sie ein beliebiges Zeichen ohne Maskierung verwenden sollten. Grundsätzlich ist die Funktion ein Makrogenerator - sie interpretiert die Werte der Argumente niemals auf besondere Weise, da seddies beim Parsen des Skripts problemlos möglich (und selbstverständlich auch möglich) ist . Stattdessen ordnet es seine Argumente nur sinnvoll in einem funktionsfähigen Skript an.
Hier ist eine Debug-Ausgabe der Funktion bei der Arbeit:
... sed " 1x;\\$2$1!{1!H;\$!d
}; \\$2$1{x;1!p;\$!d;x
}; \\$2$1!x;\\$2$1!b
s$1$1${4}$1
s$1${6}$1${7}$1${9}
s$1${10#-?}$1${11}$1${12#-?}
"
++ sed ' 1x;\d^.0d!{1!H;$!d
}; \d^.0d{x;1!p;$!d;x
}; \d^.0d!x;\d^.0d!b
sdd&&&&d
sd'\''dsqd4
sd"d\dqdg
'
Auf diese Weise lmatchkönnen Sie ganz einfach reguläre Ausdrücke auf Daten anwenden, die auf die letzte Übereinstimmung in einer Datei folgen. Das Ergebnis des Befehls, den ich oben ausgeführt habe, ist:
5" 10' 15" 20'
25" 30' 35" 40'
45" 50' 55" 60'
65" 70' 75" 80'
85" 90' 95" 100'
101010105dq 110' 115dq 120'
125dq 130' 135dq 140sq
145dq 150' 155dq 160'
165dq 170' 175dq 180'
185dq 190' 195dq 200'
... die angesichts der Teilmenge der Dateieingabe, die dem letzten Mal /^.0/folgt, übereinstimmt, die folgenden Ersetzungen anwendet:
sdd&&&&d- ersetzt $matchsich 4 mal.
sd'dsqd4 - das vierte einfache Anführungszeichen nach dem Zeilenanfang seit dem letzten Spiel.
sd"d\dqd2 - ebenso, aber für doppelte Anführungszeichen und global.
So zeigen Sie, wie Sie lmatchdas letzte Komma in einer Datei entfernen können:
printf "%d, %d %d, %d\n" $(seq 5 5 100) |
lmatch '/\(.*\),' -r\\1
AUSGABE:
5, 10 15, 20
25, 30 35, 40
45, 50 55, 60
65, 70 75, 80
85, 90 95 100