Оновлення 2020 для користувачів Linux:
Якщо у вас є версія уточненого Баша (4,4-альфа або краще), як ви , ймовірно , робити , якщо ви на Linux, то ви повинні використовувати відповідь Benjamin W. в .
Якщо ви працюєте на Mac OS, яка — останнє, що я перевірив — все ще використовувала bash 3.2, або іншим чином використовує старіший bash, перейдіть до наступного розділу.
Відповідь для bash 4.3 або раніше
Ось одне рішення для отримання вихідного сигналу find
в bash
масиві:
array=()
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done < <(find . -name "${input}" -print0)
Це складно, оскільки загалом імена файлів можуть мати пробіли, нові рядки та інші ворожі символи. Єдиний спосіб використовувати find
та безпечно відокремити імена файлів - це використання, -print0
яке друкує імена файлів, розділені нульовим символом. Це не було б великою незручністю, якби функції bash readarray
/ mapfile
functions підтримували рядки, розділені нулем, але вони цього не роблять. Це read
робить Баш, і це веде нас до цикла вище.
[Ця відповідь була спочатку написана в 2014 році. Якщо у вас остання версія bash, перегляньте оновлення нижче.]
Як це працює
Перший рядок створює порожній масив: array=()
Кожного разу, коли read
виконується оператор, із стандартного вводу зчитується ім’я файлу, розділене нулем. -r
Опція вказує , read
щоб залишити символи зворотної косої межі в поодинці. -d $'\0'
Каже про read
те , що вхід буде нульовим розділені. Так як ми опускаємо ім'я до read
, оболонка поміщає вхід в ім'я за замовчуванням: REPLY
.
Оператор array+=("$REPLY")
додає нове ім'я файлу до масиву array
.
Останній рядок поєднує в собі переспрямування та заміну команди, щоб забезпечити вихід find
на стандартний вхід while
циклу.
Навіщо використовувати заміну процесу?
Якби ми не використовували заміну процесу, цикл можна було б записати так:
array=()
find . -name "${input}" -print0 >tmpfile
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done <tmpfile
rm -f tmpfile
У наведеному вище вихідні дані find
зберігаються у тимчасовому файлі, і цей файл використовується як стандартний вхід у цикл while. Ідея заміщення процесу полягає в тому, щоб зробити такі тимчасові файли непотрібними. Отже, замість того, щоб while
цикл отримував свій stdin tmpfile
, ми можемо отримати з нього свій stdin <(find . -name ${input} -print0)
.
Заміна процесу є широко корисною. У багатьох місцях, де команда хоче читати з файлу, ви можете вказати заміну процесу <(...)
замість імені файлу. Існує аналогічна форма >(...)
, яка може бути використана замість імені файлу , де команда хоче записати в файл.
Як і масиви, заміна процесів є особливістю bash та інших вдосконалених оболонок. Він не є частиною стандарту POSIX.
Альтернатива: lastpipe
За бажанням lastpipe
може використовуватися замість заміни процесу (підказка капелюха: Цезар ):
set +m
shopt -s lastpipe
array=()
find . -name "${input}" -print0 | while IFS= read -r -d $'\0'; do array+=("$REPLY"); done; declare -p array
shopt -s lastpipe
говорить bash виконати останню команду в конвеєрі в поточній оболонці (а не у фоновому режимі). Таким чином, array
залишки існують після завершення трубопроводу. Оскільки lastpipe
набирає чинності лише якщо контроль роботи вимкнено, ми запускаємо set +m
. (У сценарії, на відміну від командного рядка, керування завданнями за замовчуванням вимкнено.)
Додаткові нотатки
Наступна команда створює змінну оболонки, а не масив оболонки:
array=`find . -name "${input}"`
Якщо ви хочете створити масив, вам потрібно буде розмістити парени навколо результату пошуку. Отже, наївно, можна було б:
array=(`find . -name "${input}"`)
Проблема полягає в тому, що оболонка виконує розбиття слів на результати, find
так що елементи масиву не гарантовано будуть такими, як ви хочете.
Оновлення 2019
Починаючи з версії 4.4-alpha, bash тепер підтримує -d
опцію, так що вищезазначений цикл більше не потрібен. Натомість можна використовувати:
mapfile -d $'\0' array < <(find . -name "${input}" -print0)
Для отримання додаткової інформації про це, дивіться (і upvote) відповідь Benjamin W. в .