Как использовать аргументы имени файла или по умолчанию для stdin, stdout (краткое)

Я хочу обрабатывать имена файлов в качестве аргументов в сценарии bash более чистым, более гибким способом, принимая аргументы 0, 1 или 2 для ввода и вывода имен файлов.

  • когда args = 0, чтение из stdin, запись в stdout
  • когда args = 1, прочитайте от $ 1, напишите в stdout
  • когда args = 2, считая от $ 1, напишите в $ 2

Как я могу сделать версию сценария bash более чистой, короче?

Вот что у меня есть сейчас, что работает, но не чисто,

#!/bin/bash if [ $# -eq 0 ] ; then #echo "args 0" fgrep -v "stuff" elif [ $# -eq 1 ] ; then #echo "args 1" f1=${1:-"null"} if [ ! -f $f1 ]; then echo "file $f1 dne"; exit 1; fi fgrep -v "stuff" $f1 elif [ $# -eq 2 ]; then #echo "args 2" f1=${1:-"null"} if [ ! -f $f1 ]; then echo "file $f1 dne"; exit 1; fi f2=${2:-"null"} fgrep -v "stuff" $f1 > $f2 fi 

Версия perl более чистая,

 #!/bin/env perl use strict; use warnings; my $f1=$ARGV[0]||"-"; my $f2=$ARGV[1]||"-"; my ($fh, $ofh); open($fh,"<$f1") or die "file $f1 failed"; open($ofh,">$f2") or die "file $f2 failed"; while(<$fh>) { if( !($_ =~ /stuff/) ) { print $ofh "$_"; } } 

5 Solutions collect form web for “Как использовать аргументы имени файла или по умолчанию для stdin, stdout (краткое)”

Я бы более активно использовал перенаправление ввода-вывода :

 #!/bin/bash [[ $1 ]] && [[ ! -f $1 ]] && echo "file $1 dne" && exit 1 [[ $1 ]] && exec 3<$1 || exec 3<&0 [[ $2 ]] && exec 4>$2 || exec 4>&1 fgrep -v "stuff" <&3 >&4 

объяснение

  • [[ $1 ]] && [[ ! -f $1 ]] && echo "file $1 dne" && exit 1

    Проверьте, был ли входной файл указан в качестве аргумента командной строки, и если файл существует.

  • [[ $1 ]] && exec 3<$1 || exec 3<&0

    Если задано значение $1 , то есть входной файл указан, указанный файл открывается в дескрипторе файла 3 , в противном случае stdin дублируется в дескрипторе файла 3 .

  • [[ $2 ]] && exec 4>$2 || exec 4>&1

    Аналогично, если задан $2 , то есть выходной файл указан, указанный файл открывается в дескрипторе файла 4 , в противном случае stdout дублируется в дескрипторе файла 4 .

  • fgrep -v "stuff" <&3 >&4

    Наконец, вызывается fgrep , перенаправляя его stdin и stdout к ранее установленным дескрипторам файлов 3 и 4 соответственно.

Повторное открытие стандартного ввода и вывода

Если вы предпочитаете не открывать промежуточные файловые дескрипторы, альтернативой является замена дескрипторов файлов, соответствующих stdin и stdout непосредственно с указанными входными и выходными файлами:

 #!/bin/bash [[ $1 ]] && [[ ! -f $1 ]] && echo "file $1 dne" && exit 1 [[ $1 ]] && exec 0<$1 [[ $2 ]] && exec 1>$2 fgrep -v "stuff" 

Недостатком такого подхода является то, что вы теряете способность отличать вывод от самого скрипта от вывода команды, которая является целью перенаправления. В исходном подходе вы можете напрямую вывести вывод сценария на unmodified stdin и stdout , которые, в свою очередь, могли быть перенаправлены вызывающим сценарием. К указанным входным и выходным файлам все равно можно получить доступ через соответствующие дескрипторы файлов, которые отличаются от сценария stdin и stdout .

Как насчет:

  input="${1:-/dev/stdin}" output="${2:-/dev/stdout}" err="${3:-/dev/stderr}" foobar <"$input" >"$output" 2>"$err" 

Следует отметить, что /dev/std(in|out|err) не входят в стандарт POSIX, поэтому это будет работать только в тех системах, которые поддерживают эти специальные файлы.

Это также предполагает разумный ввод: он не проверяет наличие файлов перед перенаправлением.

если вы не возражаете, что вывод всегда перенаправляется на stdout, вы можете использовать следующий однострочный:

 cat $1 |fgrep -v "stuff" | tee 

Вы можете использовать exec <foo для перенаправления ввода скрипта из файла foo , а также exec >foo для вывода.

 #!/bin/sh set -e if [ $# -ne 0 ]; then exec <"$1" shift fi if [ $# -ne 0 ]; then exec >"$1" shift fi fgrep -v "stuff" 

Этот стиль является кратким, но не поддается подробному описанию ошибок.

Я не знаю, является ли это «чище», но вот некоторые предложения (это не проверенный код). Использование exec (per Thomas Nyman) может привести к проблемам безопасности и к ним следует относиться с осторожностью.

Сначала повторите код в функции.

 # die <message> function die(){ echo "Fatal error: $1, exiting ..." >&2 exit 1 } # is_file <file-path> function is_file(){ [[ -n "$1" && -f "$1" ]] && return 0 die 'file not found' } 

Здесь вместо использования fgrep cat – ваш друг. Затем используйте select case:

 case $# in 0) cat ;; # accepts stdin to stdout. 1) is_file "$1"; cat "$1" ;; # puts $1 to stdout. 2) is_file "$1"; cat "$1" > "$2" ;; # puts $1 to $2. *) die 'too many arguments' ;; esac 

Другая альтернатива (чистая и очень компактная) заключается в загрузке инструкций в массив, а затем доступ к ней через значение $ #, что-то вроде указателя функции. Учитывая, что функция is_file выше, код Bash похож на:

 # action array. readonly do_stuff=( 'cat' # 0 arg. 'is_file \"$1\"; cat \"$1\"' # 1 arg. 'is_file \"$1\"; cat \"$1\" > \"$2\";' # 2 args. ) # Main - just do: [[ $# -le 2 ]] && ${do_stuff[$#]} || die 'too many arguments' 

Я не 100% от синтаксиса, но двойные кавычки должны быть экранированы. Лучше всего использовать двойные кавычки, которые содержат пути к файлам.

Добавленная заметка при записи на 2 доллара должна, вероятно, проверить, что файл не существует или он будет перезаписан.

  • Сохраните stdout и stderr в файле, затем заново создайте вывод позже
  • Как я могу вывести stdout и stderr и записать их в файл?
  • Как сделать grep в реальном журнале (stdout)?
  • stdout, stderr и протоколирование с помощью команды script
  • Какова связь между выходом программы, стандартным выходом и файловыми дескрипторами?
  • Любой способ бросить несколько строк справочной страницы, чтобы показать на stdout для справки?
  • Вращение файлов журналов во время работы
  • что подразумевается под подключением STDOUT и STDIN?
  • Как сортировать файлы отдельно без слияния?
  • Каково поведение отмененного процесса над SSH, записывающего stdout, когда сеанс SSH теряется?
  • Почему выдача одной и той же команды создает больше выходных данных в tty, чем в pts / gnome-terminal?
  • Interesting Posts
    Linux и Unix - лучшая ОС в мире.