Перевірте, чи команда видає порожній рядок


237

Як я можу перевірити, якщо команда видає порожній рядок?


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

@BasileStarynkevitch - це те, що мені потрібно. Я знаю, що команда повертає код виходу. Але в моїх командах код виходу завжди 0.так мені потрібно контролювати вихід
barp

1
Ви повинні прочитати навчальний посібник з сценаріїв Баша tldp.org/LDP/abs/html
Базиль Старинкевич

Joey Hess має корисну програму 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, оскільки він опускає символьні записи поточного ( .) та батьківського ( ..) каталогу.

Примітка. Як зазначено в коментарях, заміна команд не фіксує останніх рядків . Тому, якщо команда виводить лише нові рядки, заміна нічого не фіксує, і тест поверне помилковим. Хоча це малоймовірно, це можливо у наведеному вище прикладі, оскільки один новий рядок є дійсним іменем файлу! Більше інформації у цій відповіді .


Вихід з коду

Якщо ви хочете перевірити, що команда успішно виконана, ви можете перевірити $?, що містить вихідний код останньої команди (нуль для успіху, не нуль для відмови). Наприклад:

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
Цей метод дає помилку для команд, які виводять лише нові рядки.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

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

@netj Це дуже гарна ідея. Дивіться мою оновлену відповідь - я додав вашу пропозицію. Дуже дякую!
rsp

Будь-які ідеї, як влаштувати результат?
fahrradflucht

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

@stk Більшість програм вийде безпечно після отримання сигналу вбивства.
камбунзуальний

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

Це запустить команду і перевірить, чи має повернутий вихід (рядок) нульову довжину. Ви можете перевірити сторінки "тестування" інструкцій щодо інших прапорів.

Використовуйте аргумент "", який перевіряється, інакше порожні результати призведуть до синтаксичної помилки, оскільки не вказано другого аргументу (для перевірки)!

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


Ви можете скористатися $(замість зворотної цитати, тому що$( гніздо краще
Basile Starynkevitch

Ви маєте рацію, що краще використовувати їх завжди, хоча нам тут не потрібно гніздування.
Вегер

23

Для тих, хто хоче елегантне, базове рішення, незалежне від версій (адже воно повинно працювати в інших сучасних оболонках), а також тих, хто любить користуватися одноклассниками для швидких завдань. Ось і ми!

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

(зауважте, як один із згаданих коментарів, ls -alа насправді просто -lі -aвсе щось поверне, тому у своїй відповіді я використовую простеls


5
Якщо ви використовуєте grep -q ^замість цього, символи нового рядка також будуть відповідні, і grep нічого не надрукує до стандартного виводу. Крім того, він вийде, як тільки отримає будь-який вхід, а не чекає завершення вхідного потоку.
mortehu

Почніть з того, ls -Aякщо ви хочете включити dot-файли (або каталоги)
wrlee

13

Довідковий посібник Баша

6.4 Басові умовні вирази

-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 [[...]].
71ГА

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 деякої команди тимчасовий файл, а потім перевіряє, чи цей файл порожній. Перевага такого підходу полягає в тому, що він фіксує обидва виходи, і не використовує підкорпуси або труби. Останні аспекти є важливими, оскільки вони можуть перешкоджати захопленню керування виїзними ударами (наприклад, тут )

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.