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

93195
gilad hoch

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

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

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

49
@PlasmaPower Пока вопрос по теме на SU, * нет необходимости * задавать вопросы на другом сайте SE. Многие сайты SE имеют перекрытие; как правило, нам не нужно предлагать перекрывающийся сайт, если вопрос не является не относящимся к теме (в этом случае проголосуйте за перенос) или не относящимся к теме, но не получающим значительного ответа (в этом случае просящий должен отметить модератора- внимание / миграция, а не кросс-пост). Bob 9 лет назад 0
Связанный: [Как читать из файла или стандартного ввода в bash?] (Http://stackoverflow.com/q/6980090/55075) в SO kenorb 8 лет назад 0

8 ответов на вопрос

52
suspectus

If the file argument is the first argument to your script, test that there is an argument ($1) and that it is a file. Else read input from stdin -

So your script could contain something like this:

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

e.g. then you can call the script like

./myscript.sh filename 

or

who | ./myscript.sh 

Edit Some explanation of the script:

[ $# -ge 1 -a -f "$1" ] - If at least one command line argument ($# -ge 1) AND (-a operator) the first argument is a file (-f tests if "$1" is a file) then the test result is true.

&& is the shell logical AND operator. If test is true, then assign input="$1" and cat $input will output the file.

|| is the shell logical OR operator. If the test is false, then commands following || are parsed. input is assigned to "-". cat - reads from the keyboard.

Summarising, if the script argument is provided and it is a file, then variable input is assigned to the file name. If there is no valid argument then cat reads from the keyboard.

что означает `&& input =" $ 1 "|| input = "-" `do и почему он находится вне оператора` test`? cmo 8 лет назад 0
Я добавил правку с некоторыми пояснениями, которые, надеюсь, помогут. suspectus 8 лет назад 0
Что если скрипт имеет несколько аргументов (`$ @`)? g33kz0r 5 лет назад 0
12
Lukasz Daniluk

read reads from standard input. Redirecting it from file ( ./script <someinput ) or through pipe (dosomething | ./script) will not make it work differently.

All you have to do is to loop through all the lines in input (and it doesn't differ from iterating over the lines in file).

(sample code, processes only one line)

#!/bin/bash read var echo $var 

Will echo first line of your standard input (either through < or |).

Спасибо! я выбираю другой ответ, потому что он подходит мне лучше. я оборачивал другой сценарий, и я не хотел зацикливаться, пока не получен весь ввод (может быть много ввода ... будет расточительным). gilad hoch 9 лет назад 0
4
Jacob Hayes

You don't mention what shell you plan on using, so I'll assume bash, though these are pretty standard things across shells.

File Arguments

Arguments can be accessed via the variables $1-$n ($0 returns the command used to run the program). Say I have a script that just cats out n number of files with a delimiter between them:

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

In this case, we are passing a file name to cat. However, if you wanted to transform the data in the file (without explicitly writing and rewriting it), you could also store the file contents in a variable:

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

Read from stdin

As far as reading from stdin, most shells have a pretty standard read builtin, though there are differences in how prompts are specified (at the very least).

The Bash builtins man page has a pretty concise explanation of read, but I prefer the Bash Hackers page.

Simply:

read var_name 

Multiple Variables

To set multiple variables, just provide multiple parameter names to read:

read var1 var2 var3 

read will then place one word from stdin into each variable, dumping all remaining words into the last variable.

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

If fewer words are entered than variables, the leftover variables will be empty (even if previously set):

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

Prompts

I use -p flag often for a prompt:

read -p "Enter filename: " filename 

Note: ZSH and KSH (and perhaps others) use a different syntax for prompts:

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

Default Values

This isn't really a read trick, but I use it a lot in conjunction with read. For example:

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

Basically, if the variable (reply) exists, return itself, but if is's empty, return the following parameter ("N").

4
R..

The simplest way is to redirect stdin yourself:

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

Or if you prefer the more terse form:

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

Now the rest of your script can just read from stdin. Of course you can do similarly with more advanced option parsing rather than hard-coding the position of the filename as "$1".

exec будет пытаться выполнить аргумент как команду, что здесь не то, что нам нужно. Suzana 8 лет назад 0
@Suzana_K: Нет, когда нет аргументов, как здесь. В этом случае он просто заменяет файловые дескрипторы для самой оболочки, а не для дочернего процесса. R.. 8 лет назад 0
Я скопировал `if [" $ 1 "]; тогда exec <"$ 1"; fi` в тестовом скрипте, и он выдает сообщение об ошибке, потому что команда неизвестна. То же самое с краткой формой. Suzana 8 лет назад 0
@Suzana_K: Какую оболочку вы используете? Если это так, то это не рабочая реализация команды POSIX sh / оболочки Bourne. R.. 8 лет назад 1
GNU bash 4.3.11 на Linux Mint Qiana Suzana 8 лет назад 0
@Suzana_K: Я только что протестировал с bash 4.3.33, и он работает нормально. Я подозреваю, что где-то в скрипте есть опечатка или какая-то странная конфигурация. Вы вводите команду в интерактивном режиме или в сценарии, который вы выполняете? Возможно, вы могли бы вставить скрипт, если хотите, чтобы я посмотрел на него (или еще лучше, разместите здесь вопрос о том, почему он не работает, несмотря на то, что должен). R.. 8 лет назад 0
Да это странно Я также хотел бы знать причину. Я разместил вывод и сценарий в [этой сути] (https://gist.github.com/SuzanaK/04a1a66b2fe77f107013). Suzana 8 лет назад 0
@Suzana_K Эта ошибка возникает из-за того, что exec пытается открыть файл hallo, который не существует. Если вы хотите * записать * в файл, переверните `<` to `>`. YoYoYonnY 6 лет назад 0
3
Aaron Davies

use (or chain off of) something else that already behaves this way, and use "$@"

let's say i want to write a tool that will replace runs of spaces in text with tabs

tr is the most obvious way to do this, but it only accepts stdin, so we have to chain off of 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' $ 

for an example where the tool being used already behaves this way, we could reimplement this with sed instead:

$ cat entab2.sh #!/bin/sh sed -r 's/ +/\t/g' "$@" $ 
2
Daniel

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

#!/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="$"  # You can now use "$input_file" as your file to operate on cat "$input_file" 

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

Это фантастика! Я использую `uglifyjs </ dev / stdin`, и это прекрасно работает! bfred.it 8 лет назад 1
0
RaamEE

You can also keep it simple and use this code


When you create a script file pass_it_on.sh with this code,

#!/bin/bash cat 

You can run

cat SOMEFILE.txt | ./pass_it_on.sh 

and all the contents of stdin will be simply spewed out to the screen.


Alternatively use this code to both keep a copy of stdin in a file and then spew it out to the screen.

#!/bin/bash tmpFile=`mktemp` cat > $tmpFile cat $tmpFile 

and here is another example, maybe more readable, explained here:

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

#!/bin/bash VALUE=$(cat) echo "$VALUE" 

Have fun.

RaamEE

0
kenorb

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

file=$ 

что эквивалентно $.

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

while IFS= read -r line; do printf '%s\n' "$line" # Or: env POSIXLY_CORRECT=1 echo "$line" done < <(cat -- "$file") 

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