Яка різниця між && та | в баш-скрипті?


13

Візьмемо два рядки нижче, які дають нам два різні результати.

p=$(cd ~ && pwd) ; echo $p
p=$(cd ~ |  pwd) ; echo $p

Чим вони відрізняються?


@heemayl Дякую Чи можете ви вияснити відмінності в моєму випадку? Дякую.
Нам Г ВУ

1
Підказка: Команди з обох боків |виконуються в підрозділах .
heemayl

Відповіді:


21

В p=$(cd ~ && pwd):

  • Підстановка команд $(), працює в нижній частині

  • cd ~змінить каталог на ~(ваш будинок), якщо cdуспішно ( &&), то pwdнадрукує ім'я каталогу в STDOUT, отже, рядок, збережений на, pбуде вашим домашнім каталогом, наприклад/home/foobar

В p=$(cd ~ | pwd):

  • Знову $()породжує передпластя

  • Команди з обох сторін |виконуються у відповідних підшах (і обидва починаються одночасно)

  • так cd ~це робиться як в підпакеті, так і pwdв окремій підпакеті

  • таким чином, ви отримаєте тільки STDOUT, pwdтобто звідки ви запускаєте команду, це може бути будь-який каталог, як ви можете собі уявити, отже, pбуде містити ім'я каталогу, звідки викликається команда, а не ваш домашній каталог


Яке призначення труби між командами? cd ~не дає жодного результату і pwdне читає жодного вводу.
Бармар

Друга команда по суті еквівалентна, (cd ~);p=$(pwd)чи не так?
Бармар

@Barmar Так, це те, що використовувала ОП, і я просто пояснюю це для нього.
heemayl

7

Основне питання полягає в тому, як оператори &&і |з'єднають дві команди.

&&Поєднує команди з допомогою коду виходу. |З'єднує дві команди з допомогою дескрипторів файлів (стандартного пристрою введення, стандартний висновок).

Давайте спочатку спростимо. Ми можемо видалити завдання та написати:

echo $(cd ~ && pwd)
echo $(cd ~ | pwd)

Для аналізу цього ми можемо навіть видалити підрозділ виконання команд:

$ cd ~ && pwd
$ cd ~ | pwd

&&

Якщо ми змінимо підказку, щоб показати каталог, де виконуються команди, щось подібне PS1='\w\$ ', ми побачимо це:

/tmp/user$ cd ~ && pwd
/home/user
~$ 
  • Команда cd ~змінила "каталог каталогів" на дім фактичного користувача, який виконує команду ( /home/user).
  • Оскільки результат команди був успішним (код виходу 0), наступна команда після && виконується
  • І "теперішній робочий каталог" друкується.
  • Запуску оболонки змінила своє , pwdщоб , ~як показано на запрошення ~$.

Якщо зміна каталогу чомусь не вдалася (код виходу не 0) з якоїсь причини (каталог не існує, дозволи блокують зчитування каталогу), наступна команда не буде виконана.

Приклад:

/tmp/user$ false && pwd
/tmp/user$ _ 

Код виходу 1 з falseперешкоджає виконанню наступної команди.

Таким чином, вихідний код "команди 1" - це те, що впливає на "команду 2".

Тепер, ефекти всієї команди:

/tmp/user$ echo $(cd ~ && pwd)
/home/user
/tmp/user$ _

Каталог було змінено, але всередині підрозділу $(…)змінений каталог друкується /home/user, але негайно відкидається, коли під-оболонка закривається. Pwd повертається в початковий каталог ( /tmp/user).

|

Ось що відбувається:

/tmp/user$ cd ~ | pwd
/tmp/user
/tmp/user$ _

Метасимвол |(не справжній оператор) передає сигнал оболонці, щоб створити те, що називається "Труба", (в bash), кожна команда з кожної сторони труби ( |) встановлюється всередині кожної власної підкошти, спочатку правої сторони команда, значить, ліва. Дескриптор вхідного файлу ( /dev/stdin) правої команди підключається до дескриптора виводу ( /dev/stdout), після чого обидві команди запускаються та залишаються для взаємодії зліва. Ліва команда ( cd -) не має виходу, і, також, права команда ( pwd) не приймає введення. Отже, кожен працює незалежно всередині кожної власної підколі.

  • cd ~Змінює PWD однієї оболонки.
  • pwdДрукує (повністю незалежну) PWD іншого суб-оболонки.

Зміни на кожній оболонці відміняються, коли труба закінчується, зовнішня під оболонка не змінила pwd.

Ось чому дві команди з'єднані лише "дескрипторами файлів".
У цьому випадку нічого не надсилається і нічого не читається.

Вся команда:

$ echo "$(cd ~ | pwd)"

Буде просто надрукувати каталог, де виконувалася команда.


5

Я не впевнений, чи ти мав на увазі "|" або '||' у вашому другому випадку.

'|' в оболонці передається вихід однієї команди на вхід іншої - загальний випадок використання є чимось на зразок: curl http://abcd.com/efgh | grep ijkl тобто виконайте команду та використовуйте іншу команду для обробки результатів команди.

У наведеному прикладі він є досить безглуздим, оскільки "cd", як правило, не генерує жодного результату, а "pwd" не очікує жодного введення.

'&&' та '||' є командами партнера. Вони розроблені так, щоб використовуватись так само, як і логічні оператори "та" і "або" на більшості мов. Однак оптимізації, які виконуються, дають їм специфічну поведінку, яка є парадигмою програмування оболонок.

Щоб визначити результат логічної операції "і", вам потрібно лише оцінити другу умову, якщо перша умова успішна - якщо перша умова не виконана, загальний результат завжди буде помилковим.

Щоб визначити результат логічної операції "чи", вам потрібно лише оцінити другу умову, якщо перша умова не виконана - якщо перша умова вдається, загальний результат завжди буде істинним.

Отже, в оболонці, якщо у вас є, command1 && command2 command2він буде виконаний лише після command1завершення та повернення успішного коду результату. Якщо у вас command1 || command2 command2буде виконано, коли command1завершено, якщо command1повертається код відмови.

Іншою поширеною парадигмою є command1тестова команда - це генерує єдиний рядок оператора if / then - наприклад:

[ "$VAR" = "" ] && VAR="Value if empty"

Це (довгомотовий) спосіб присвоєння значення змінній, якщо вона наразі порожня.

Існує багато прикладів використання цього процесу в інших місцях на Stack Exchange

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.