Сценарий Bash, который читает имена файлов из канала или из командной строки args?

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

7
14.01.2013, 02:41
5 ответов

Можно использовать 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

(Не протестированный!)

3
27.01.2020, 20:16

Инструменты обработки текста традиционно читают вход из стандартного входа, когда Вы не указываете имени файла на командной строке. Можно проверить, существуют ли параметры командной строки путем тестирования $# переменная. Принятие этого 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
5
27.01.2020, 20:16
  • 1
    , "Это возможно, конечно, но я не советую ему, потому что это необычно". Это важно. Важно кодировать способом, который предсказуем для Ваших пользователей и потенциальных будущих специалистов по обслуживанию. Кроме того, @Gilles, маленькое придирается к мелочам, но Ваше первое предложение испытывает недостаток в надлежащем глаголе ;) –  rahmu 14.01.2013, 02:41
  • 2
    , я не знал <код> $ #-eq 0 </код> часть, Спасибо! я понял, что моей проблемой было больше comp.licated, но я отмечу это право. яйцо –  Jeff 14.01.2013, 02:42

Это скрипт, который я написал, чтобы безопасно открывать файлы как в 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
1
27.01.2020, 20:16

Это работает для меня:

for filename in ${*:-`cat`}; do
    process_file "$filename"
done

${*:-`cat`}

сначала развернет аргументы командной строки. Если он не определен (подстановка параметров bash :- ), то будет использоваться stdin из трубы.

1
27.01.2020, 20:16

С bash4.4 и выше:

if [ "$#" -eq 0 ]; then
  readarray -d '' args
  set -- "${args[@]}"
fi

for arg do
   something with "$arg"
done
1
27.01.2020, 20:16

Теги

Похожие вопросы