50

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

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

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

8 ответов8

53

Если аргумент файла является первым аргументом в вашем сценарии, проверьте, есть ли аргумент ($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" файл), то результат теста верен.

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

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

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

12

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

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

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

#!/bin/bash

read var
echo $var

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

4

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

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

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

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

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

4

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

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

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

#!/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

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

Что касается чтения из stdin, большинство оболочек имеют довольно стандартную встроенную функцию 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").

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' "$@"
$ 
2

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

#!/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, смотрите это.

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")
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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .