Не можете передати базу "картографічного файлу" Баша ... але чому?


13

Я просто хочу перенести всі файли в певному каталозі в масив bash (якщо припустити, що жоден з файлів не має нової лінії у назві):

Тому:

myarr=()
find . -maxdepth 1  -name "mysqldump*" | mapfile -t myarr; echo "${myarr[@]}"

Порожній результат!

Якщо я використовую круговий спосіб використання файлу, тимчасового чи іншого способу:

myarr=()
find . -maxdepth 1  -name "mysqldump*" > X
mapfile -t myarray < X
echo "${myarray[@]}"

Результат!

Але чому не mapfileчитає належним чином з труби?



Відмінні відповіді навколо, дякую всім. Цікаво, як стратегія виконання трубопроводу (кожна частина, що працює в процесі говоріння), просочується «вгору» та змінює видиме значення коду, в основному мовчки ставлячи «локальне» перед кожною змінною, що з’являється в трубі. Мовою, яка є чимось відмінним від шаленого клею для інших програм, це буде помилка, сподіваємось.
Девід Тонхофер

2
Якщо ви надаєте код shellcheck , ви отримуватимете попередження: SC2030 : "Модифікація var локальна (для підрозділу, спричиненого трубопроводом)" і SC2031 : "var був змінений у підзарядці. Ця зміна може бути втрачена." . Відмінно.
Девід Тонхофер

Чому використання findі mapfileвзагалі тут , а не просто myarr=(mysqldump*)? Це навіть буде працювати з іменами з пробілами та новими рядками.
BlackJack

1
Щойно помітив, що потрібно увімкнути nullglobопцію ( shopt -s nullglob), myarr=(mysqldump*)щоб не закінчити масив ('mysqldump*')у випадку, якщо жоден файл не збігається.
Девід Тонхофер

Відповіді:


25

Від man 1 bash:

Кожна команда в конвеєрі виконується як окремий процес (тобто в підпакеті).

Такі підшари успадковують змінні від основної оболонки, але вони не залежать. Це означає, що mapfileу вашій оригінальній команді діє самостійно myarr. Потім echo(знаходячись поза трубою) друкує порожню myarr(що є основною оболонкою myarr).

Ця команда працює інакше:

find . -maxdepth 1 -name "mysqldump*" | { mapfile -t myarr; echo "${myarr[@]}"; }

В цьому випадку mapfileі echoдіють на той же myarr(який не є основною оболонкою є myarr).

Щоб змінити основну оболонку, myarrвам потрібно mapfileточно запустити в основній оболонці. Приклад:

myarr=()
mapfile -t myarr < <(find . -maxdepth 1 -name "mysqldump*")
echo "${myarr[@]}"

Додано посилання на "процес заміщення", як зазначено у відповіді Атті, якщо у відвідувача є момент TL; DR.
Девід Тонхофер

11

Bash запускає команди конвеєра в середовищі підклітини, тому будь-які змінні призначення тощо, які відбуваються в ньому, не видно решті оболонки.

Dash (Debian's /bin/sh) і зайняті файли shсхожі, тоді як zsh і ksh виконують останню частину в основній оболонці. У Bash ви можете використовувати shopt -s lastpipeте ж саме, але він працює лише тоді, коли контроль роботи відключений, тому за замовчуванням не в інтерактивних оболонках.

Тому:

$ bash -c 'x=a; echo b | read x; echo $x'
a
$ bash -c 'shopt -s lastpipe; x=a; echo b | read x; echo $x'
b

( readі mapfileте саме.)

В якості альтернативи (і, як згадує Attie), використовуйте процес заміщення , який працює як узагальнена труба і підтримується в Bash, ksh і zsh.

$ bash -c 'x=a; read x < <(echo b); echo $x'
b

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


2
Якщо ви відключите контроль роботи Bash, ви можете також використовувати lastpipe в інтерактивній оболонці:set +m; shopt -s lastpipe; x=a; echo b | read x; echo $x; set -m
Сайрус

@Cyrus, ага, я забув подробиці, дякую
ilkkachu

9

Як зазначив Каміль, кожен елемент трубопроводу - це окремий процес.

Ви можете використати таку заміну процесу , щоб findпрацювати в іншому процесі, з mapfileвикликом , що залишаються в поточному інтерпретатор, надаючи доступ до myarrзгодом:

myarr=()
mapfile -t myarr < <( find . -maxdepth 1  -name "mysqldump*" )
echo "${myarr[@]}"

b < <( a )буде діяти аналогічно з a | bточки зору того, як проводиться трубопровід - різниця в тому, що bвиконується " тут ".

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