10

Я просто хочу получить все файлы в определенном каталоге в массив 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 не читает правильно из канала?

3 ответа3

23

От 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[@]}"
10

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

Dash (Debian's /bin/sh), а также busybox 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 оставляет это неопределенным, если части конвейера работают в подоболочках или нет, так что нельзя сказать, что какая-либо из оболочек будет "неправильной" в этом.

8

Как отметил Камиль, каждый элемент в конвейере - это отдельный процесс.

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

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

b < <( a ) будет действовать аналогично a | b с точки зрения того, как конвейер подключен - разница в том, что b выполняется « здесь ».

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