Як написати скрипт, який приймає вхід з файлу або з stdin?


57

Як можна написати сценарій, який приймає вхід або з аргументу імені файлу, або з stdin?

наприклад, ви можете використовувати lessцей спосіб. можна виконати less filenameі рівнозначно cat filename | less.

чи є простий спосіб "поза коробкою" зробити це? чи мені потрібно переосмислити колесо і написати трохи логіки у сценарії?


@PlasmaPower Поки мова йде про тему на СУ, немає вимоги , щоб запитати на іншому сайті SE. Дуже багато сайтів ДП ​​перекриваються; як правило, нам не потрібно пропонувати веб-сайт, що перекривається, якщо питання не є темою (у такому випадку голосувати за перехід) або тематично, але не отримувати великої кількості відповідей (у такому випадку, запитуючий повинен позначити права модератора, увага / міграція, а не перехресне повідомлення).
Боб

Відповіді:


59

Якщо аргумент файлу є першим аргументом у вашому скрипті, перевірте, чи є аргумент ( $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) AND (-a оператор) перший аргумент є файлом (-f тести, якщо "$ 1" є файлом), то результат тесту є істинним.

&&є логічним оператором AND оболонки. Якщо тест істинний, то призначте input="$1"і cat $inputвиведете файл.

||є оператором оболонки логічним АБО. Якщо тест хибний, то наступні команди ||аналізуються. вхід призначається "-". Команда cat -зчитується з клавіатури.

Підводячи підсумок, якщо аргумент сценарію надано і це файл, то ім'я файлу призначається змінний вхід. Якщо аргументу немає, кішка читає з клавіатури.


що робить && input="$1" || input="-" і чому це поза testоператором?
cmo

Я додав редагування з деякими поясненнями, які, сподіваюся, допоможуть.
підозрюваний

Що робити, якщо сценарій має кілька аргументів ( $@)?
g33kz0r

12

readзчитується зі стандартного вводу. Перенаправлення його з файлу ( ./script <someinput) або через pipe ( dosomething | ./script) не призведе до того, що він буде працювати інакше.

Все, що вам потрібно зробити, - це проглянути всі рядки, що вводяться (і це не відрізняється від повторення у рядку у файлі).

(зразок коду, обробляє лише один рядок)

#!/bin/bash

read var
echo $var

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


Дякую! я вибираю іншу відповідь, тому що вона мені більше підходила. я завершував інший сценарій, і я не хотів циклічно, поки весь вхід не отримав (може бути багато входу ... буде марно).
gilad hoch

4

Ви не згадуєте, яку оболонку ви плануєте використовувати, тому я вважаю, що це башти, хоча це досить стандартні речі в оболонках.

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

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

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

Що стосується читання з stdin, більшість оболонок мають досить стандартний readвбудований характер, хоча існують відмінності в тому, як вказано підказки (принаймні).

Сторінка " Bash buildins man" має досить стисле пояснення 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}

В основному, якщо змінна (відповідь) існує, поверніться, але якщо вона порожня, поверніть наступний параметр ("N").


4

Найпростіший спосіб - перенаправити stdin самостійно:

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

Або якщо ви віддаєте перевагу більш лаконічній формі:

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

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


execспробуємо виконати аргумент як команду, яка тут не є тим, що ми хочемо.
Сузана

@Suzana_K: Не тоді, коли у нього немає аргументів, як тут. У цьому випадку він просто замінює дескриптори файлів для самої оболонки, а не дочірній процес.
Р ..

Я скопіював if [ "$1" ] ; then exec < "$1" ; fiтестовий скрипт, і він дає повідомлення про помилку, оскільки команда невідома. Те саме з лаконічною формою.
Сузана

1
@Suzana_K: Яку оболонку ви використовуєте? Якщо це правда, це не робоча реалізація команди POSIX sh / оболонки Bourne.
Р ..

GNU Баш 4.3.11 на Linux Mint Qiana
Сюзана

3

використовувати (або відключати) щось інше, що вже так поводиться, і використовувати "$@"

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

trце найочевидніший спосіб зробити це, але він приймає лише stdin, тому нам доводиться відключати 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і це чудово працює!
fregante

0

Ви також можете зробити це просто і використовувати цей код


Коли ви створюєте файл сценарію pass_it_on.sh з цим кодом,

#!/bin/bash

cat

Можна бігати

cat SOMEFILE.txt | ./pass_it_on.sh

і весь вміст stdin буде просто виведений на екран.


Крім того, використовуйте цей код, щоб зберігати копію stdin у файлі, а потім виводити його на екран.

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


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")
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.