lcomma() { sed '
$x;$G;/\(.*\),/!H;//!{$!d
}; $!x;$s//\1/;s/^\n//'
}
Ciò dovrebbe rimuovere solo l'ultima occorrenza di a ,
in qualsiasi file di input e stamperà comunque quelle in cui a ,
non si verifica. Fondamentalmente, buffer sequenze di righe che non contengono una virgola.
Quando incontra una virgola, scambia il buffer di riga corrente con il buffer di mantenimento e in questo modo stampa simultaneamente tutte le righe che si sono verificate dall'ultima virgola e libera il buffer di conservazione.
Stavo solo scavando nel mio file di cronologia e ho trovato questo:
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
)\"";}
In realtà è abbastanza buono. Sì, lo usa eval
, ma non gli passa mai nulla oltre un riferimento numerico ai suoi argomenti. Costruisce sed
script arbitrari per gestire un'ultima partita. Ti mostrerò:
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...'
Che stampa quanto segue su stderr. Questa è una copia lmatch
dell'input:
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'
La eval
subshell ed della funzione scorre una volta tutti gli argomenti. Mentre cammina su di essi, itera un contatore in modo appropriato a seconda del contesto di ogni switch e salta su molti argomenti per la successiva iterazione. Da allora in poi fa una delle poche cose per argomento:
- Per ogni opzione aggiunta
$a
al parser di opzioni $o
. $a
è assegnato in base al valore di $i
cui è incrementato dal conteggio arg per ogni arg elaborato. $a
viene assegnato uno dei due seguenti valori:
a=$((i+=1))
- questo viene assegnato se a un'opzione corta non è stato aggiunto l'argomento o se l'opzione era lunga.
a=$i#-?
- questo viene assegnato se l'opzione è breve e non avere il suo arg allegati.
a=\${$a}${1:+$d\${$(($1))\}}
- Indipendentemente dall'assegnazione iniziale, $a
il valore viene sempre racchiuso tra parentesi graffe e - in un -s
caso - a volte $i
viene incrementato di un altro e viene aggiunto un campo delimitato ulteriormente.
Il risultato è che eval
non viene mai passata una stringa contenente eventuali incognite. A ciascuno degli argomenti della riga di comando viene fatto riferimento il loro numero di argomento numerico, anche il delimitatore che viene estratto dal primo carattere del primo argomento ed è l'unica volta in cui dovresti usare qualunque carattere senza caratteri di escape. Fondamentalmente, la funzione è un generatore di macro: non interpreta mai i valori degli argomenti in un modo speciale perché sed
può (e ovviamente,) gestirlo facilmente quando analizza lo script. Invece, organizza in modo ragionevole i suoi argomenti in una sceneggiatura praticabile.
Ecco alcuni output di debug della funzione al lavoro:
... 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
'
E così lmatch
può essere usato per applicare facilmente regex ai dati che seguono l'ultima corrispondenza in un file. Il risultato del comando che ho eseguito sopra è:
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'
... che, dato il sottoinsieme del file di input che segue l'ultima volta che /^.0/
corrisponde, applica le seguenti sostituzioni:
sdd&&&&d
- Sostituisce $match
con se stesso 4 volte.
sd'dsqd4
- la quarta virgoletta singola che segue l'inizio della riga dall'ultima partita.
sd"d\dqd2
- idem, ma per virgolette doppie e globalmente.
E così, per dimostrare come si potrebbe usare lmatch
per rimuovere l'ultima virgola in un file:
printf "%d, %d %d, %d\n" $(seq 5 5 100) |
lmatch '/\(.*\),' -r\\1
PRODUZIONE:
5, 10 15, 20
25, 30 35, 40
45, 50 55, 60
65, 70 75, 80
85, 90 95 100