Проверьте, выводит ли команда пустую строку


237

Как я могу проверить, выводит ли команда пустую строку?


7
Команда не возвращает строку (она возвращает небольшой целочисленный код завершения, обычно 0 при успехе и 1 или 2 при ошибке), но она может вывести некоторые данные для помещения в строку.
— Василий Старынкевич,

@BasileStarynkevitch - это то, что мне нужно. Я знаю, что команда возвращает код выхода. Но код выхода моей команды всегда равен 0., поэтому мне нужно контролировать вывод
— barp

1
Вы должны прочитать некоторые Bash скрипты учебник tldp.org/LDP/abs/html
— Basile Starynkevitch

У Джои Хесса есть утилита ifneдля этого. joeyh.name/code/moreutils
— tripleee

Ответы:


309

Ранее задавался вопрос, как проверить, есть ли файлы в каталоге. Следующий код достигает этого, но см . Ответ rsp для лучшего решения.


Пустой вывод

Команды не возвращают значения - они выводят их. Вы можете захватить этот вывод, используя подстановку команд ; например $(ls -A). Вы можете проверить непустую строку в Bash следующим образом:

if [[ $(ls -A) ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Обратите внимание, что я использовал -Aвместо -a, так как он пропускает символические записи каталога current ( .) и parent ( ..).

Примечание. Как указано в комментариях, подстановка команд не захватывает завершающие символы новой строки . Следовательно, если команда выводит только новые строки, подстановка ничего не захватывает, а тест возвращает false. Хотя это очень маловероятно, это возможно в приведенном выше примере, поскольку одна новая строка является допустимым именем файла! Больше информации в этом ответе .


Код выхода

Если вы хотите проверить, успешно ли выполнена команда, вы можете проверить $?, в которой содержится код завершения последней команды (ноль для успеха, ненулевое значение для сбоя). Например:

files=$(ls -A)
if [[ $? != 0 ]]; then
    echo "Command failed."
elif [[ $files ]]; then
    echo "Files found."
else
    echo "No files found."
fi

Больше информации здесь .


4
Вы можете опустить -n , так что это будет простоif [[ $(ls -A) ]]; then
— пользователь

6
Этот метод выдает false для команд, которые выводят только символы новой строки.
— Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

89

TL; DR

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then ...; fi

Спасибо netj за предложение улучшить мой оригинал:
if [[ $(ls -A | wc -c) -ne 0 ]]; then ...; fi


Это старый вопрос, но я вижу, по крайней мере, две вещи, которые нуждаются в некотором улучшении или, по крайней мере, в некотором уточнении.

Первая проблема

Первая проблема, которую я вижу, состоит в том, что большинство приведенных здесь примеров просто не работают . Они используют ls -alи ls -Alкоманды - оба из которых выход не пустые строки в пустых каталогов. Эти примеры всегда сообщают, что есть файлы, даже если их нет .

По этой причине вы должны использовать просто ls -A- Почему кто-то захочет использовать -lпереключатель, который означает «использовать формат длинного списка», когда все, что вам нужно, это проверить, есть ли какие-либо выходные данные или нет?

Поэтому большинство ответов здесь просто неверны.

Вторая проблема

Вторая проблема заключается в том , что в то время как некоторые ответы работают нормально (те , которые не используют ls -alили , ls -Alно ls -Aвместо этого) все они делают что - то вроде этого:

  1. запустить команду
  2. буферизировать весь вывод в ОЗУ
  3. преобразовать вывод в огромную однострочную строку
  4. сравнить эту строку с пустой строкой

Вместо этого я бы предложил сделать следующее:

  1. запустить команду
  2. считать символы в своем выводе, не сохраняя их
    • или даже лучше - посчитайте количество максимально 1 символом using head -c1
      (спасибо netj за публикацию этой идеи в комментариях ниже)
  3. сравнить это число с нулем

Так, например, вместо:

if [[ $(ls -A) ]]

Я хотел бы использовать:

if [[ $(ls -A | wc -c) -ne 0 ]]
# or:
if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]

Вместо того:

if [ -z "$(ls -lA)" ]

Я хотел бы использовать:

if [ $(ls -lA | wc -c) -eq 0 ]
# or:
if [ $(ls -lA | head -c1 | wc -c) -eq 0 ]

и так далее.

Для небольших выходов это может не быть проблемой, но для больших выходов разница может быть значительной:

$ time [ -z "$(seq 1 10000000)" ]

real    0m2.703s
user    0m2.485s
sys 0m0.347s

Сравните это с:

$ time [ $(seq 1 10000000 | wc -c) -eq 0 ]

real    0m0.128s
user    0m0.081s
sys 0m0.105s

И даже лучше:

$ time [ $(seq 1 10000000 | head -c1 | wc -c) -eq 0 ]

real    0m0.004s
user    0m0.000s
sys 0m0.007s

Полный пример

Обновленный пример из ответа Уилла Воудена:

if [[ $(ls -A | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Обновляется снова после предложений от netj :

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Дополнительное обновление от jakeonfire :

grepвыйдет с ошибкой, если совпадений нет. Мы можем воспользоваться этим, чтобы немного упростить синтаксис:

if ls -A | head -c1 | grep -E '.'; then
    echo "there are files"
fi

if ! ls -A | head -c1 | grep -E '.'; then
    echo "no files found"
fi

Отбрасывание пробелов

Если команда, которую вы тестируете, может вывести некоторые пробелы, которые вы хотите рассматривать как пустую строку, то вместо:

| wc -c

Вы можете использовать:

| tr -d ' \n\r\t ' | wc -c

или с head -c1:

| tr -d ' \n\r\t ' | head -c1 | wc -c

или что-то вроде того.

Резюме

  1. Сначала используйте команду, которая работает .

  2. Во-вторых, избегайте ненужного хранения в оперативной памяти и обработки потенциально огромных данных.

В ответе не указывалось, что выходные данные всегда малы, поэтому необходимо учитывать возможность больших выходных данных.


3
[[ $(... | head -c | wc -c) -gt 0 ]]или [[ -n $(... | head -c1) ]]лучше, потому wc -cчто придется использовать весь вывод команды.
— нет

@netj Это очень хорошая идея. Смотрите мой обновленный ответ - я добавил ваше предложение. Большое спасибо!
— RSP

Любые идеи о том, как получить выход?
— fahrradflucht

Я думаю, что оригинал (без головы) лучше в общем случае. Не всегда хорошая идея убить команду, как только она начнет писать вывод!
— СТК

@stk Большинство программ благополучно завершат работу после получения сигнала уничтожения.
— cambunctious

40
if [ -z "$(ls -lA)" ]; then
  echo "no files found"
else
  echo "There are files"
fi

Это запустит команду и проверит, имеет ли возвращаемый вывод (строка) нулевую длину. Возможно, вы захотите проверить справочные страницы «test» на наличие других флагов.

Используйте «» вокруг проверяемого аргумента, иначе пустые результаты приведут к синтаксической ошибке, поскольку второй аргумент (для проверки) не задан!

Обратите внимание, что ls -la всегда возвращается .и ..поэтому использование этого не будет работать, см. Справочные страницы . Кроме того, хотя это может показаться удобным и легким, я полагаю, что оно легко сломается. Написание небольшого скрипта / приложения, которое возвращает 0 или 1 в зависимости от результата, гораздо надежнее!


Вы можете предпочесть использовать $(вместо обратной цитаты, потому что $(гнездо лучше
— Василий Старынкевич

Вы правы, лучше практиковать их всегда, хотя нам здесь не нужно.
— Вегер

23

Для тех, кто хочет элегантное решение, не зависящее от версии bash (на самом деле должно работать в других современных оболочках), и тех, кто любит использовать однострочники для быстрых задач. Вот так!

ls | grep . && echo 'files found' || echo 'files not found'

(обратите внимание, как один из упомянутых комментариев, ls -alи на самом деле, просто -lи -aвсе что-то вернут, поэтому в своем ответе я использую простойls


5
Если вы используете grep -q ^вместо этого, символы новой строки также будут сопоставлены, и grep не будет ничего выводить на стандартный вывод. Кроме того, он выйдет, как только получит какие-либо входные данные, вместо того, чтобы ждать окончания входного потока.
— mortehu

Должно начинаться с того, ls -Aесли вы хотите включить точечные файлы (или каталоги)
— wrlee

13

Справочное руководство Bash

6.4 Условные выражения Bash

-z string
     True if the length of string is zero.

-n string
string
     True if the length of string is non-zero.

Вы можете использовать сокращенную версию:

if [[ $(ls -A) ]]; then
  echo "there are files"
else
  echo "no files found"
fi

1
В скобках отсутствует -z или -n [[...]].
— 71GA

4
@ 71GA: Возможно, это легко упустить из виду, но если вы посмотрите внимательно, вы увидите, что это просто stringсиноним -n string.
— пользователь

10

Как прокомментировал Джон Лин, ls -alвсегда будет выводить (для .и ..). Вы хотите ls -Alизбежать этих двух каталогов.

Например, вы можете поместить вывод команды в переменную оболочки:

v=$(ls -Al)

Старая, нестабильная запись

v=`ls -Al`

но я предпочитаю вложенные обозначения $(...)

Вы можете проверить, если эта переменная не пуста

if [ -n "$v" ]; then
    echo there are files
else
    echo no files
fi

И вы могли бы объединить как if [ -n "$(ls -Al)" ]; then


5

Я предполагаю, что вы хотите вывод ls -alкоманды, поэтому в bash вы получите что-то вроде:

LS=`ls -la`

if [ -n "$LS" ]; then
  echo "there are files"
else
  echo "no files found"
fi

Это не работает: команда lsникогда не выполняется. Вы только проверяете, является ли раскрытие переменной LS непустым.
— gniourf_gniourf

1
@gniourf_gniourf - что заставляет вас так думать? Заклинание обратного удара, по общему признанию, не так красиво и гибко, как $ (), но оно работает ...
— tink

-aВыключатель никогда не будет возвращать никакого содержания (то есть, он будет возвращать содержимое , есть ли файлы или нет) , так как всегда есть неявная .и ..каталоги присутствуют.
— wrlee

3

Вот решение для более экстремальных случаев:

if [ `command | head -c1 | wc -c` -gt 0 ]; then ...; fi

Это будет работать

  • для всех раковин Борна;
  • если команда выводит все нули;
  • эффективно независимо от выходного размера;

тем не мение,

  • команда или ее подпроцессы будут уничтожены после вывода чего-либо.

1

Все приведенные ответы касаются команд, которые завершают и выводят непустую строку.

Большинство из них сломаны в следующих смыслах:

  • Они не работают должным образом с командами, выводящими только новые строки;
  • начиная с Bash≥4.4, большинство из них будет содержать стандартную ошибку, если команда выводит нулевые байты (поскольку они используют подстановку команд);
  • большинство будет отбрасывать полный поток вывода, поэтому дождется завершения команды до ответа. Некоторые команды никогда не завершаются (попробуйте, например, yes).

Таким образом, чтобы исправить все эти проблемы и эффективно ответить на следующий вопрос,

Как я могу проверить, выводит ли команда пустую строку?

ты можешь использовать:

if read -n1 -d '' < <(command_here); then
    echo "Command outputs something"
else
    echo "Command doesn't output anything"
fi

Вы можете также добавить некоторые тайм - аут для того, чтобы тест выдает ли команда не пустую строку в данный момент времени, используя read«s -tварианта. Например, в течение 2,5 секунд:

if read -t2.5 -n1 -d '' < <(command_here); then
    echo "Command outputs something"
else
    echo "Command doesn't output anything"
fi

Замечание. Если вы считаете, что вам нужно определить, выводит ли команда непустую строку, у вас, скорее всего, проблема XY.


Я думаю, что этот ответ недооценивается. Я производительность тестирования некоторые из решений здесь , используя 100 итераций для каждого из 3 -х входных сценариев ( seq 1 10000000, seq 1 20и printf""). Единственный матч, который этот подход чтения не выиграл, был с -z "$(command)"подходом для пустого ввода. Даже тогда он проигрывает только на 10-15%. Кажется, они безубыточны seq 1 10. Несмотря на это, -z "$(command)"подход становится настолько медленным с ростом размера ввода, что я бы избегал использовать его для любого ввода переменного размера.
— Abathur

0

Вот альтернативный подход, который записывает std-out и std-err некоторой команды во временный файл, а затем проверяет, является ли этот файл пустым. Преимущество этого подхода состоит в том, что он захватывает оба выхода и не использует подоболочки или каналы. Эти последние аспекты важны, потому что они могут помешать перехвату обработки выхода bash (например, здесь )

tmpfile=$(mktemp)
some-command  &> "$tmpfile"
if [[ $? != 0 ]]; then
    echo "Command failed"
elif [[ -s "$tmpfile" ]]; then
    echo "Command generated output"
else
    echo "Command has no output"
fi
rm -f "$tmpfile"

0

Иногда вы хотите сохранить вывод, если он не пустой, чтобы передать его другой команде. Если это так, вы можете использовать что-то вроде

list=`grep -l "MY_DESIRED_STRING" *.log `
if [ $? -eq 0 ]
then
    /bin/rm $list
fi

Таким образом, rmкоманда не будет зависать, если список пуст.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.