Как читать stdin, когда аргументы не передаются?
Script не работает, когда я хочу использовать стандартный ввод, если нет аргументов (файлов). Есть ли способ использования stdin вместо файла в этом коде?
Я пробовал это:
if [ ! -n $1 ] # check if argument exists
then
$1=$(</dev/stdin) # if not use stdin as an argument
fi
var="$1"
while read line
do
... # find the longest line
done <"$var"
Ответы
Ответ 1
Просто замените bash специально интерпретированный /dev/stdin
как имя файла:
VAR=$1
while read blah; do
...
done < "${VAR:-/dev/stdin}"
(Обратите внимание, что bash фактически использует этот специальный файл /dev/stdin
если он создан для ОС, который его предлагает, но поскольку bash 2.04 будет работать это отсутствие файла в системах, которые его не поддерживают.)
Ответ 2
Для общего случая, когда вы хотите прочитать значение из stdin при отсутствии параметра, это будет работать.
$ echo param | script.sh
$ script.sh param
script.sh
#!/bin/bash
set -- "${1:-$(</dev/stdin)}" "${@:2}"
echo $1
Ответ 3
Переменные присваиваются значениям Var=Value
, и эта переменная используется, например. echo $Var
. В вашем случае это будет
1=$(</dev/stdin)
при назначении стандартного ввода. Однако я не думаю, что имена переменных можно начинать с символа цифры. См. Вопрос bash чтение из файла или stdin способов устранения этого.
Ответ 4
Вот моя версия script:
#!/bin/bash
file=${1--} # POSIX-compliant; ${1:--} can be used either.
while IFS= read -r line; do
printf '%s\n' "$line"
done < <(cat -- "$file")
Если файл отсутствует в аргументе, прочитайте его со стандартного ввода.
Дополнительные примеры: Как читать из файла или stdin в bash? в stackoverflow SE
Ответ 5
ответ пиквера обеспечивает элегантное решение; это объяснение , почему подход OP не работал.
Основная проблема с подходом OP заключалась в попытке назначить позиционный параметр $1
с помощью $1=...
, который не будет работать.
LHS расширяется оболочкой до значения $1
, и результат интерпретируется как имя переменной, которой нужно назначить, - явно, а не намерение.
единственный способ назначить $1
в bash через set
встроенный.
Предостережение состоит в том, что set
неизменно устанавливает все позиционные параметры, поэтому вы должны включить и другие, если они есть.
set -- "${1:-/dev/stdin}" "${@:2}" # "${@:2}" expands to all remaining parameters
(Если вы ожидаете только не более 1 аргумента, set -- "${1:-/dev/stdin}"
будет делать.)
Вышеупомянутая также исправляет вторичную проблему с OP-подходом: попытка сохранить содержимое, а не имя файла stdin в $1
, так как используется <
.
${1:-/dev/stdin}
представляет собой приложение bash расширение параметра, в котором говорится: вернуть значение $1
, если $1
undefined (аргумент не передан) или его значение - пустая строка (""
или ''
). Изменение ${1-/dev/stdin}
(no :
) вернет только /dev/stdin
, если $1
- undefined (если оно содержит любое значение, даже пустую строку, оно будет возвращено).
Если собрать все вместе:
# Default to filename '/dev/stdin' (stdin), if none was specified.
set -- "${1:-/dev/stdin}" "${@:2}"
while read -r line; do
... # find the longest line
done < "$1"
Но, конечно, гораздо более простой подход заключался бы в том, чтобы использовать ${1:-/dev/stdin}
как имя файла напрямую:
while read -r line; do
... # find the longest line
done < "${1:-/dev/stdin}"
или через промежуточную переменную:
filename=${1:-/dev/stdin}
while read -r line; do
... # find the longest line
done < "$filename"