Оновлення 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/ mapfilefunctions підтримували рядки, розділені нулем, але вони цього не роблять. Це 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. в .