Я играю со скриптами Bash. Я бы хотел присвоить результат команды переменной и код возврата другой.

Напр .:

line_count=$(cat "$filename" | wc -l) #If the file does not exist, an error code is returned
cmd_return_code=$? #I want to assign the previous error code to a variable

Я пытался использовать $? как показано выше, но он захватывает код возврата самого предыдущего назначения, который равен 0.

Как я могу это сделать?

Спасибо

2 ответа2

3

Проблема здесь в том, что в конвейере cat "$filename" | wc -l , когда файл не существует, cat выйдет с ошибкой, но wc -l успешно посчитает 0 строк текста, полученных от cat . Состояние выхода последней команды в конвейере рассматривается как окончательное состояние конвейера в целом, поэтому весь конвейер считается успешным. Как это:

$ cat nosuchfile | wc -l
cat: nosuchfile: No such file or directory
       0
$ echo "$?"
0

Обычно в bash вы можете получить статусы отдельной команды в конвейере с массивом PIPESTATUS , например:

$ cat nosuchfile | wc -l
cat: nosuchfile: No such file or directory
       0
$ echo "${PIPESTATUS[@]}"
1 0

... но это не работает с конвейером в расширении команды, таком как $( ) , потому что этот конвейер будет выполняться в подоболочке, а PIPESTATUS не может распространяться из подоболочки; только окончательный статус передается обратно в родительскую оболочку:

$ line_count=$(cat nosuchfile | wc -l)
cat: nosuchfile: No such file or directory
$ echo "${PIPESTATUS[@]}"
0

Итак, что вы можете сделать по этому поводу? Ну, как сказал l0b0, одна возможность - установить pipefail . Вам не нужно делать это для всего сценария, вы можете установить его только для этой конкретной подоболочки, выполнив это в подстановке команд:

$ line_count=$(set -o pipefail; cat nosuchfile | wc -l)
cat: nosuchfile: No such file or directory
$ echo "$?"
1

Для этой конкретной команды вы также можете исключить конвейер (это то, что называется бесполезным использованием cat или UUOC), и иметь wc чтения непосредственно из файла, либо передавая имя файла в качестве параметра:

$ line_count=$(wc -l nosuchfile)
wc: nosuchfile: open: No such file or directory
$ echo $?
1

... или используя перенаправление ввода:

$ line_count=$(wc -l <nosuchfile)
-bash: nosuchfile: No such file or directory
$ echo $?
1

Есть несколько различий между этими двумя параметрами: если вы передадите имя файла в wc (и оно существует), оно выведет имя файла, а также количество строк:

$ line_count=$(wc -l realfile.txt)
$ echo "$line_count"
       6 realfile.txt

... в то время как с опцией перенаправления не будет. Кроме того, со вторым параметром оболочка отвечает за открытие файла (и передачу дескриптора открытого файла команде wc), поэтому в случае неудачи именно оболочка выдает ошибку (обратите внимание, что сообщение об ошибке исходит от «-bash» , а не wc) и wc никогда не запускается.

2

Код выхода назначения подстановки команд - это код выхода подстановки команд. Посмотрите, например, код выхода foo="$(false)" .

Чтобы сделать код выхода конвейера кодом выхода первой ошибочной команды в конвейере:

set -o pipefail

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