Как написать скрипт, который принимает входные данные из файла или из стандартного ввода?

57

Как можно написать скрипт, который принимает входные данные из аргумента имени файла или из стандартного ввода?

например, вы могли бы использовать lessэтот способ. можно выполнить less filenameи эквивалентно cat filename | less.

Есть ли простой способ «из коробки» сделать это? или мне нужно заново изобрести колесо и написать немного логики в скрипте?

Гилад Хох
источник
@PlasmaPower Пока вопрос по теме на SU, нет необходимости задавать вопросы на другом сайте SE. Многие сайты SE имеют перекрытие; как правило, нам не нужно предлагать перекрывающийся сайт, если вопрос не является не относящимся к теме (в этом случае проголосуйте за перенос) или не относящимся к теме, но не получающим значительного ответа (в этом случае просящий должен отметить модератора- внимание / миграция, а не кросс-пост).
Боб

Ответы:

59

Если аргумент file является первым аргументом в вашем скрипте, проверьте, что существует аргумент ( $1) и что это файл. Еще прочитайте ввод от stdin -

Таким образом, ваш скрипт может содержать что-то вроде этого:

#!/bin/bash
[ $# -ge 1 -a -f "$1" ] && input="$1" || input="-"
cat $input

например, тогда вы можете назвать сценарий как

./myscript.sh filename

или же

who | ./myscript.sh

Изменить некоторые объяснения сценария:

[ $# -ge 1 -a -f "$1" ]- Если хотя бы один аргумент командной строки ( $# -ge 1) И (оператор -a) первым аргументом является файлом (-f проверяет, является ли «$ 1» файлом), то результат теста равен true.

&&является логическим оператором оболочки. Если проверка верна, назначьте input="$1"и cat $inputвыведите файл.

||является логическим оператором оболочки ИЛИ Если тест ложен, то следующие команды ||анализируются. вход назначен на «-». Команда cat -читает с клавиатуры.

Подводя итог, если аргумент скрипта предоставлен и это файл, тогда переменной ввода назначается имя файла. Если нет допустимого аргумента, то cat читает с клавиатуры.

suspectus
источник
что делает && input="$1" || input="-" и почему вне testоператора?
Cmo
Я добавил правку с некоторыми пояснениями, которые, надеюсь, помогут.
Подозреваемый
Что если скрипт имеет несколько аргументов ( $@)?
g33kz0r
12

readчитает со стандартного ввода. Перенаправление его из file ( ./script <someinput) или pipe ( dosomething | ./script) не заставит его работать по-другому.

Все, что вам нужно сделать, это перебрать все строки на входе (и это не отличается от перебора строк в файле).

(пример кода, обрабатывает только одну строку)

#!/bin/bash

read var
echo $var

Отобразится первая строка вашего стандартного ввода (либо через, <либо |).

Лукаш Данилюк
источник
Спасибо! я выбираю другой ответ, потому что он подходит мне лучше. я оборачивал другой скрипт, и я не хотел зацикливаться, пока не получен весь ввод (может быть много ввода ... было бы расточительно).
Гилад Хох
4

Вы не упоминаете, какую оболочку вы планируете использовать, поэтому я буду использовать bash, хотя это довольно стандартные вещи для всех оболочек.

Аргументы файла

Аргументы могут быть доступны через переменные $1- $n( $0возвращает команду, использованную для запуска программы). Скажем, у меня есть скрипт, который просто catвыводит n файлов с разделителем между ними:

#!/usr/bin/env bash
#
# Parameters:
#    1:   string delimiter between arguments 2-n
#    2-n: file(s) to cat out
for arg in ${@:2} # $@ is the array of arguments, ${@:2} slices it starting at 2.
do
   cat $arg
   echo $1
done

В этом случае мы передаем имя файла в cat. Однако, если вы хотите преобразовать данные в файл (без явной записи и перезаписи), вы также можете сохранить содержимое файла в переменной:

file_contents=$(cat $filename)
[...do some stuff...]
echo $file_contents >> $new_filename

Читать со стандартного ввода

Что касается чтения из стандартного ввода, большинство оболочек имеют довольно стандартную readвстроенную функцию , хотя существуют различия в том, как указываются приглашения (по крайней мере).

Страница руководства по Bash buildins содержит довольно краткое объяснение read, но я предпочитаю страницу Bash Hackers .

Просто:

read var_name

Несколько переменных

Чтобы установить несколько переменных, просто укажите несколько имен параметров read:

read var1 var2 var3

read затем поместит одно слово из stdin в каждую переменную, сбросив все оставшиеся слова в последнюю переменную.

λ read var1 var2 var3
thing1 thing2 thing3 thing4 thing5
λ echo $var1; echo $var2; echo $var3
thing1
thing2
thing3 thing4 thing5

Если введено меньше слов, чем переменных, оставшиеся переменные будут пустыми (даже если они были установлены ранее):

λ read var1 var2 var3
thing1 thing2
λ echo $var1; echo $var2; echo $var3
thing1
thing2
# Empty line

Запрашивает

Я -pчасто использую флаг для подсказки:

read -p "Enter filename: " filename

Примечание: ZSH и KSH (и, возможно, другие) используют другой синтаксис для запросов:

read "filename?Enter filename: " # Everything following the '?' is the prompt

Значения по умолчанию

Это не совсем readтрюк, но я часто использую его вместе с read. Например:

read -p "Y/[N]: " reply
reply=${reply:-N}

В принципе, если переменная (reply) существует, верните себя, но если она пуста, верните следующий параметр («N»).

Джейкоб Хейс
источник
4

Самый простой способ - перенаправить stdin самостоятельно:

if [ "$1" ] ; then exec < "$1" ; fi

Или, если вы предпочитаете более краткую форму:

test "$1" && exec < "$1"

Теперь остальная часть вашего скрипта может просто читать со стандартного ввода. Конечно, вы можете сделать то же самое с более сложным синтаксическим анализом, вместо того, чтобы жестко задавать положение имени файла как "$1".

Р..
источник
execбудет пытаться выполнить аргумент в виде команды, которая не является тем, что мы хотим здесь.
Suzana
@Suzana_K: Нет, когда нет аргументов, как здесь. В этом случае он просто заменяет файловые дескрипторы для самой оболочки, а не для дочернего процесса.
Р ..
Я скопировал if [ "$1" ] ; then exec < "$1" ; fiв тестовый скрипт, и он выдает сообщение об ошибке, потому что команда неизвестна. То же самое с краткой формой.
Suzana
1
@Suzana_K: Какую оболочку вы используете? Если это правда, это не рабочая реализация команды POSIX sh / оболочки Bourne.
Р ..
GNU Баш 4.3.11 на Linux Mint Qiana
Сюзана
3

использовать (или цепочку) что-то еще, что уже ведет себя таким образом, и использовать "$@"

скажем, я хочу написать инструмент, который заменит пробелы в тексте на вкладки

trэто самый очевидный способ сделать это, но он принимает только стандартный ввод, поэтому мы должны отцепить cat:

$ cat entab1.sh
#!/bin/sh

cat "$@"|tr -s ' ' '\t'
$ cat entab1.sh|./entab1.sh
#!/bin/sh

cat     "$@"|tr -s      '       '       '\t'
$ ./entab1.sh entab1.sh
#!/bin/sh

cat     "$@"|tr -s      '       '       '\t'
$ 

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

$ cat entab2.sh
#!/bin/sh

sed -r 's/ +/\t/g' "$@"
$ 
Аарон Дэвис
источник
3

Вы также можете сделать:

#!/usr/bin/env bash

# Set variable input_file to either $1 or /dev/stdin, in case $1 is empty
# Note that this assumes that you are expecting the file name to operate on on $1
input_file="${1:-/dev/stdin}"

# You can now use "$input_file" as your file to operate on
cat "$input_file"

Для более аккуратных фокусов подстановки параметров в Bash см это .

Даниил
источник
1
Это фантастика! Я пользуюсь uglifyjs < /dev/stdinи работает чудесно!
Фреганте
0

Вы также можете сделать это простым и использовать этот код


Когда вы создаете файл сценария pass_it_on.sh с этим кодом,

#!/bin/bash

cat

Вы можете запустить

cat SOMEFILE.txt | ./pass_it_on.sh

и все содержимое стандартного ввода будет просто выбрасываться на экран.


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

#!/bin/bash

tmpFile=`mktemp`
cat > $tmpFile
cat $tmpFile    

и вот еще один пример, возможно, более читаемый, объясненный здесь:

http://mockingeye.com/blog/2013/01/22/reading-everything-stdin-in-a-bash-script/

#!/bin/bash

VALUE=$(cat)

echo "$VALUE"

Веселиться.

RaamEE

RaamEE
источник
0

Самый простой способ и POSIX-совместимый:

file=${1--}

что эквивалентно ${1:--}.

Затем прочитайте файл как обычно:

while IFS= read -r line; do
  printf '%s\n' "$line" # Or: env POSIXLY_CORRECT=1 echo "$line"
done < <(cat -- "$file")
kenorb
источник