lcomma() { sed '
$x;$G;/\(.*\),/!H;//!{$!d
}; $!x;$s//\1/;s/^\n//'
}
Это должно удалить только последнее вхождение a ,в любом входном файле - и он все равно будет печатать те, в которых a ,не встречается. По сути, он буферизует последовательности строк, которые не содержат запятой.
Когда он встречает запятую, он заменяет текущий буфер строк на буфер удержания и, таким образом, одновременно распечатывает все строки, которые произошли после последней запятой, и освобождает свой буфер удержания.
Я просто копался в моем файле истории и нашел это:
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
)\"";}
Это на самом деле очень хорошо. Да, он использует eval, но никогда не передает ему ничего, кроме числовой ссылки на его аргументы. Он создает произвольные sedсценарии для обработки последнего совпадения. Я покажу тебе:
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...'
Это выводит следующее на stderr. Это копия lmatchввода:
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'
Функция evaled subshell повторяет все свои аргументы один раз. Проходя по ним, он выполняет итерацию счетчика в зависимости от контекста для каждого переключателя и пропускает столько аргументов для следующей итерации. С тех пор он делает одну из нескольких вещей на аргумент:
- Для каждой опции парсер опций добавляет
$aк $o. $aназначается на основе значения, $iкоторое увеличивается на число аргументов для каждого обработанного аргумента.$aприсваивается одно из двух следующих значений:
a=$((i+=1)) - это присваивается, если к короткой опции не добавлен аргумент, или если опция была длинной.
a=$i#-?- это назначается , если опция является коротким и вовсе есть ее аргумент прилагается к нему.
a=\${$a}${1:+$d\${$(($1))\}}- Независимо от начального присваивания $aзначение всегда заключено в фигурные скобки и - в -sслучае - иногда $iувеличивается на единицу и добавляется дополнительное поле с разделителями.
В результате evalникогда не передается строка, содержащая какие-либо неизвестные. Каждый из аргументов командной строки именуется их числовым номером аргумента - даже разделителем, который извлекается из первого символа первого аргумента и является единственным случаем, когда вы должны использовать любой символ, который не экранирован. По сути, функция является генератором макросов - она никогда не интерпретирует значения аргументов каким-либо особым образом, потому что sedможет (и, конечно, легко ) справиться с этим при синтаксическом анализе сценария. Вместо этого он просто разумно организует свои аргументы в работоспособный скрипт.
Вот некоторые отладочные выходные данные функции на работе:
... 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
'
И поэтому lmatchего можно использовать для простого применения регулярных выражений к данным после последнего совпадения в файле. Результат команды, которую я запустил выше:
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'
... который с учетом подмножества файла, который следует за последним /^.0/совпадением, применяет следующие замены:
sdd&&&&d- заменяет $matchсобой 4 раза.
sd'dsqd4 - четвертая одинарная кавычка, следующая за началом строки после последнего матча.
sd"d\dqd2 - то же самое, но для двойных кавычек и глобально.
Итак, чтобы продемонстрировать, как можно использовать lmatchдля удаления последней запятой в файле:
printf "%d, %d %d, %d\n" $(seq 5 5 100) |
lmatch '/\(.*\),' -r\\1
ВЫХОД:
5, 10 15, 20
25, 30 35, 40
45, 50 55, 60
65, 70 75, 80
85, 90 95 100