Как читать из файла или stdin в Bash?
В Perl следующий код будет считываться из файла, указанного в командной строке args или из stdin:
while () { print($_); }
Это очень удобно. Я просто хочу знать, что является самым простым способом чтения из файла или stdin в bash.
- Как отправить ввод на консоль, как если бы пользователь печатал?
- Сравнить два дерева каталогов
- setvbuf не может сделать stdin небуферизованным
- Как читать из stdin с помощью fgets ()?
- Как прочитать строку, введенную пользователем в C?
- Использование fseek с указателем файла, указывающим на stdin
- Почему я получаю «Необработанный тип исключения IOException»?
- Стоит ли вообще заглядывать в буфер stdin?
- Можно ли установить тайм-аут для std :: cin?
- Почему мы не можем читать один символ за раз из System.in?
- Использование fflush (stdin)
- запустить exe / process с stdin stdout и stderr?
- Чтение из stdin flush stdout?
Следующее решение читается из файла, если сценарий вызывается с именем файла в качестве первого параметра $1
отличие от стандартного ввода.
while read line do echo "$line" done < "${1:-/dev/stdin}"
Подстановка ${1:-...}
принимает $1
если в противном случае используется имя файла стандартного ввода собственного процесса.
Возможно, самым простым решением является redirect stdin с помощью слияния оператора перенаправления:
#!/bin/bash less <&0
Stdin - дескриптор файла. Вышеуказанное отправляет входной stream в ваш скрипт bash в stdin less.
Подробнее о перенаправлении дескриптора файла .
Вот простейший способ:
#!/bin/sh cat -
Применение:
$ echo test | sh my_script.sh test
Чтобы назначить stdin для переменной, вы можете использовать: STDIN=$(cat -)
или просто просто STDIN=$(cat)
поскольку оператор не нужен (согласно комментарию @ mklement0 ).
Чтобы проанализировать каждую строку со стандартного ввода , попробуйте выполнить следующий скрипт:
#!/bin/bash while IFS= read -r line; do printf '%s\n' "$line" done
Чтобы прочитать из файла или stdin (если аргумент отсутствует), вы можете расширить его до:
#!/bin/bash file=${1--} # POSIX-compliant; ${1:--} can be used either. while IFS= read -r line; do printf '%s\n' "$line" # Or: env POSIXLY_CORRECT=1 echo "$line" done < <(cat -- "$file")
Заметки:
-
read -r
- Не обрабатывать символ обратной косой черты каким-либо особым образом. Рассмотрим каждую обратную косую черту как часть входной строки.- Без установки
IFS
по умолчанию последовательности Space и Tab в начале и в конце строк игнорируются (обрезаются).- Используйте
printf
вместоecho
чтобы избежать печати пустых строк, когда строка состоит из одного-e
,-n
или-E
. Однако существует обходное решение, используяenv POSIXLY_CORRECT=1 echo "$line"
который выполняет ваше внешнееecho
GNU, которое его поддерживает. См .: Как эхо «-e»?
См .: Как читать stdin, когда аргументы не передаются? в stackoverflow SE
Решение echo
добавляет новые строки, когда IFS
разрывает входной stream. Ответ @ fgm может быть немного изменен:
cat "${1:-/dev/stdin}" > "${2:-/dev/stdout}"
Я думаю, что это прямолинейный путь:
$ cat reader.sh #!/bin/bash while read line; do echo "reading: ${line}" done < /dev/stdin
-
$ cat writer.sh #!/bin/bash for i in {0..5}; do echo "line ${i}" done
-
$ ./writer.sh | ./reader.sh reading: line 0 reading: line 1 reading: line 2 reading: line 3 reading: line 4 reading: line 5
Цикл Perl в вопросе читается из всех аргументов имени файла в командной строке или из стандартного ввода, если файлы не указаны. Ответы, которые я вижу, кажутся обработкой одного файла или стандартного ввода, если файл не указан.
Хотя часто высмеивается как UUOC (бесполезное использование cat
), бывают случаи, когда cat
является лучшим инструментом для работы, и можно утверждать, что это один из них:
cat "[email protected]" | while read -r line do echo "$line" done
Единственным недостатком этого является то, что он создает конвейер, работающий в под-оболочке, поэтому такие вещи, как присвоения переменных в цикле while, недоступны вне конвейера. Метод bash
вокруг этого процесса заключается в замене процесса :
while read -r line do echo "$line" done < <(cat "[email protected]")
Это оставляет цикл while в основной оболочке, поэтому переменные, установленные в цикле, доступны вне цикла.
Поведение Perl с кодом, приведенным в OP, может принимать не один или несколько аргументов, и если аргумент представляет собой один дефис, это понимается как stdin. Кроме того, всегда возможно иметь имя файла с $ARGV
. Ни один из ответов, представленных до сих пор, действительно имитирует поведение Perl в этих отношениях. Вот чистая возможность Bash. Трюк заключается в том, чтобы использовать exec
соответствующим образом.
#!/bin/bash (($#)) || set -- - while (($#)); do { [[ $1 = - ]] || exec < "$1"; } && while read -r; do printf '%s\n' "$REPLY" done shift done
Имя файла доступно в $1
.
Если аргументы не заданы, мы искусственно устанавливаем -
как первый позиционный параметр. Затем мы переходим к параметрам. Если параметр отсутствует, мы перенаправляем стандартный ввод из имени файла с помощью exec
. Если это redirect удастся, мы зацикливаемся с циклом while. Я использую стандартную переменную REPLY
, и в этом случае вам не нужно сбросить IFS
. Если вы хотите другое имя, вы должны сбросить IFS
так (если, конечно, вы этого не хотите и знаете, что вы делаете):
while IFS= read -r line; do printf '%s\n' "$line" done
Точнее…
while IFS= read -r line ; do printf "%s\n" "$line" done < file
Пожалуйста, попробуйте следующий код:
while IFS= read -r line; do echo "$line" done < file
Код ${1:-/dev/stdin}
будет просто понимать первый аргумент, так что как насчет этого.
ARGS='$*' if [ -z "$*" ]; then ARGS='-' fi eval "cat -- $ARGS" | while read line do echo "$line" done
Следующее работает со стандартным sh
(проверено dash
на Debian) и вполне читаемо, но это вопрос вкуса:
if [ -n "$1" ]; then cat "$1" else cat fi | commands_and_transformations
Подробности: если первый параметр не пуст, то cat
этот файл, иначе cat
стандартный ввод. Затем вывод целого оператора if
обрабатывается commands_and_transformations
.
Я не считаю, что любые из этих ответов приемлемы. В частности, принятый ответ обрабатывает только первый параметр командной строки и игнорирует остальные. Программа Perl, которую он пытается эмулировать, обрабатывает все параметры командной строки. Поэтому принятый ответ даже не отвечает на вопрос. Другие ответы используют расширения bash, добавляют ненужные команды «cat», работают только для простого случая эхо ввода на выходе или просто излишне сложны.
Тем не менее, я должен отдать им кредит, потому что они дали мне некоторые идеи. Вот полный ответ:
#!/bin/sh if [ $# = 0 ] then DEFAULT_INPUT_FILE=/dev/stdin else DEFAULT_INPUT_FILE= fi # Iterates over all parameters or /dev/stdin for FILE in "[email protected]" $DEFAULT_INPUT_FILE do while IFS= read -r LINE do # Do whatever you want with LINE here. echo $LINE done < "$FILE" done
Как насчет
for line in `cat`; do something($line); done
-for line in `cat`; do something($line); done