Sed не отслеживает в обратном порядке: после того как это обработало строку, это сделано. Так “находят строку и печатают предыдущие строки N”, не собирается работать, как, в отличие от этого, “находят строку и печатают следующие строки N”, на котором легко привить.
Если файл не является слишком длинным, так как Вы, кажется, соглашаетесь с расширениями GNU, можно использовать tac
инвертировать строки файла.
tac | sed '/banana/I,+2 d' | tac
Другой угол атаки состоит в том, чтобы поддержать раздвижное окно в инструменте как awk. Адаптация от Является там какой-либо альтернативой-A grep-B-C переключатели (для печати немногих строк прежде и после)? (предупреждение: минимально протестированный):
#!/bin/sh
{ "exec" "awk" "-f" "$0" "$@"; } # -*-awk-*-
# The array h contains the history of lines that are eligible for being "before" lines.
# The variable skip contains the number of lines to skip.
skip { --skip }
match($0, pattern) { skip = before + after }
NR > before && !skip { print NR h[NR-before] }
{ delete h[NR-before]; h[NR] = $0 }
END { if (!skip) {for (i=NR-before+1; i<=NR; i++) print h[i]} }
Использование: /path/to/script -v pattern='banana' -v before=2
Можно использовать xargs
преобразовать STDIN
(даже с большим количеством записей, широко больше, чем буфер командной строки) к аргументам.
Или иначе, возможно, что-то как:
if [ $# -gt 0 ] ;then
for filename in "$@" ; do
process_file "$filename"
done
else
IFS=$'\n' read -d '' -r -a filenames
for filename in "${filenames[@]}"; do
process_file "$filename"
done
fi
или оба:
IFS=$'\n' read -d '' -r -a filenames
for filename in "${filenames[@]}" "$@" ; do
process_file "$filename"
done
fi
или путем добавления такой опции, как -l
выдержите аргументы строки только, удостовериться, чтобы никакое чтение не (ожидало) от STDIN
:
case "$1" in
-l )
shift
for filename in "$@" ; do
process_file "$filename"
done
;;
* )
for filename in "${filenames[@]}" "$@" ; do
process_file "$filename"
done
;;
esac
(Не протестированный!)
Инструменты обработки текста традиционно читают вход из стандартного входа, когда Вы не указываете имени файла на командной строке. Можно проверить, существуют ли параметры командной строки путем тестирования $#
переменная. Принятие этого process_file
чтения от стандартного входа, если Вы не передаете его аргумент:
if [ $# -eq 0 ]; then
process_file
else
for x; do
process_file "$x"
done
fi
С другой стороны, если process_file
требует аргумента, попытайтесь передать его /dev/stdin
читать из стандартного входа:
if [ $# -eq 0 ]; then set /dev/stdin; fi
for x; do
process_file "$x"
done
Вы попросили читать список имен файлов от стандартного входа, когда нет никаких параметров командной строки. Это возможно, конечно, но я не советую ему, потому что это необычно. Ваши пользователи должны будут изучить конвенцию, это отличается от большинства других инструментов. Тем не менее, вот один способ сделать это:
if [ $# -eq 0 ]; then
set -f # turn off globbing
IFS='
' # set newlines to be the only field separator
set -- $(cat)
set +f; unset IFS
fi
for x; do
process_file "$x"
done
Это скрипт, который я написал, чтобы безопасно открывать файлы как в cygwin X11 gvim, так и в windows gvim. Он спроектирован как оболочка оболочки, и это часть, которая делает то, что вы хотите:
if [ ${#} -ne 0 ]; then #if we have filenames as CLArgs...
[[ ! -z ${DEBUG} ]] && echo "Got arguments [${#}]:'${@}'"
for OFILE in "${@}"; do
[[ -h ${OFILE} ]] && OFILE="$(readlink -f "${OFILE}")"
[[ ${WINVIM} == true ]] && OFILE=$(cygpath -wal "${OFILE}")
echo "\"${VIMRUN}\" --servername GVIM $RT \"${OFILE}\""
"${VIMRUN}" --servername GVIM $RT "${OFILE}" &
RT="--remote-tab"
done
else #otherwise read from stdin safely and handle spaces...
while read OFILE; do
[[ -h ${OFILE} ]] && OFILE="$(readlink -f "${OFILE}")"
[[ ${WINVIM} == true ]] && OFILE=$(cygpath -wal "${OFILE}")
echo "\"${VIMRUN}\" --servername GVIM $RT \"${OFILE}\""
"${VIMRUN}" --servername GVIM $RT "${OFILE}" &
RT="--remote-tab"
done
fi
Это работает для меня:
for filename in ${*:-`cat`}; do
process_file "$filename"
done
${*:-`cat`}
сначала развернет аргументы командной строки. Если он не определен (подстановка параметров bash :- ), то будет использоваться stdin
из трубы.
С bash4.4
и выше:
if [ "$#" -eq 0 ]; then
readarray -d '' args
set -- "${args[@]}"
fi
for arg do
something with "$arg"
done