lcomma() { sed '
$x;$G;/\(.*\),/!H;//!{$!d
}; $!x;$s//\1/;s/^\n//'
}
Cela ne devrait supprimer que la dernière occurrence d'un ,
dans n'importe quel fichier d'entrée - et il imprimera toujours ceux dans lesquels un ,
ne se produit pas. Fondamentalement, il met en mémoire tampon des séquences de lignes qui ne contiennent pas de virgule.
Quand il rencontre une virgule, il échange la mémoire tampon de ligne actuelle avec la mémoire tampon de maintien et de cette manière imprime simultanément toutes les lignes qui se sont produites depuis la dernière virgule et libère sa mémoire tampon de conservation.
Je creusais simplement mon fichier historique et j'ai trouvé ceci:
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
)\"";}
C'est en fait assez bon. Oui, il utilise eval
, mais il ne lui passe jamais rien au-delà d'une référence numérique à ses arguments. Il construit des sed
scripts arbitraires pour gérer une dernière correspondance. Je vais te montrer:
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...'
Cela imprime ce qui suit à stderr. Ceci est une copie de lmatch
l'entrée de:
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'
Le eval
sous-shell ed de la fonction parcourt une fois tous ses arguments. En les parcourant, il itère un compteur de manière appropriée en fonction du contexte de chaque commutateur et ignore autant d'arguments pour la prochaine itération. À partir de là, cela fait l'une des quelques choses par argument:
- Pour chaque option, l'analyseur d'options s'ajoute
$a
à $o
. $a
est attribué en fonction de la valeur $i
qui est incrémentée par le nombre d'arguments pour chaque arg traité. $a
se voit attribuer l'une des deux valeurs suivantes:
a=$((i+=1))
- ceci est attribué si une option courte n'a pas d'argument ajouté ou si l'option était longue.
a=$i#-?
- ceci est attribué si l'option est courte et que son argument lui est ajouté.
a=\${$a}${1:+$d\${$(($1))\}}
- Indépendamment de l'affectation initiale, $a
la valeur de est toujours entourée d'accolades et - dans un -s
cas - $i
est parfois incrémentée de plus et un champ supplémentaire délimité est ajouté.
Le résultat est qu'on eval
ne passe jamais une chaîne contenant des inconnues. Chacun des arguments de ligne de commande est référencé par son numéro d'argument numérique - même le délimiteur qui est extrait du premier caractère du premier argument et est la seule fois où vous devez utiliser le caractère non échappé. Fondamentalement, la fonction est un générateur de macros - elle n'interprète jamais les valeurs des arguments d'une manière spéciale car elle sed
peut (et le fera, bien sûr) facilement gérer cela lorsqu'elle analyse le script. Au lieu de cela, il organise simplement judicieusement ses arguments dans un script réalisable.
Voici une sortie de débogage de la fonction au travail:
... 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
'
Et lmatch
peut donc être utilisé pour appliquer facilement des expressions rationnelles aux données après la dernière correspondance dans un fichier. Le résultat de la commande que j'ai exécutée ci-dessus est:
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'
... qui, étant donné le sous-ensemble de l'entrée de fichier qui suit la dernière mise en /^.0/
correspondance, applique les substitutions suivantes:
sdd&&&&d
- se remplace $match
par lui-même 4 fois.
sd'dsqd4
- le quatrième guillemet simple suivant le début de la ligne depuis le dernier match.
sd"d\dqd2
- idem, mais pour les guillemets doubles et globalement.
Et donc, pour montrer comment on pourrait utiliser lmatch
pour supprimer la dernière virgule d'un fichier:
printf "%d, %d %d, %d\n" $(seq 5 5 100) |
lmatch '/\(.*\),' -r\\1
PRODUCTION:
5, 10 15, 20
25, 30 35, 40
45, 50 55, 60
65, 70 75, 80
85, 90 95 100