На основеhttps://stackoverflow.com/a/2957781/53897:
echo MQSeriesRuntime_5-U200491-7.5.0-4.x86_64 | perl -n -e '/_([^-]+)/ && print $1'
find... -exec sh -c 'sh code' {} +
В оболочке bash/ksh запускает команду find
в новом процессе, который запускает один или несколько вызовов команд sh
. sh code
сможет влиять только на переменные вызова sh
, который интерпретирует этот код (, кстати, sh
не предназначен для поддержки массивов ).
Если вы хотите сохранить пути к файлам, найденным с помощью find
, в массив текущей оболочки, именно эта оболочка должна выполнить назначение.
С bash
4.4+,вы можете сделать это с помощью:
readarray -td '' F_ARR < <(find "$CDIR" ! -type d -mtime +7 -print0)
Если ваш find
не поддерживает -print0
, вы можете заменить его на -exec printf '%s\0' {} +
.
(с более старыми версиями bash
, см. , как вы можете это сделать в этом ответе на аналогичный вопрос).
Здесь вы также можете использовать оболочку zsh
и выполнить:
f_arr=($CDIR/**/*(NDm+7^/))
Отличие от вышеизложенного заключается в том, что:
oN
glob, чтобы отключить сортировку $CDIR
сам по себе не будет включен, даже если ему больше 8 дней. $CDIR
является символической ссылкой на каталог, zsh
все равно будет спускаться в него. Поскольку вы отметили свой вопрос как bash
, так и ksh
, обратите внимание, что существуют разные реализации и варианты ksh (ksh88, ksh93, грядущий ksh2020 для (изначально )Дэвид Корн, pdksh и его производные, такие как OpenBSD sh
или mksh
, к которым вы можете добавить режим эмуляции ksh )zsh.
Большинство функций bash
исходят из ksh88
и ksh93
, некоторые — из zsh
и mksh
. readarray
— одна из немногих функций, встроенных в bash
и не встречающихся больше нигде.
ksh93 добавил параметр -d
к read
в 1993 году, позже скопированный bash (2000 )zsh (2003 )и mksh (2011 ), но только bash, zsh, mksh и ksh2020 могут использовать его с пустым разделителем для обработки записей с разделителями NUL. Подстановка процесса была добавлена в ksh86 (1986 ), но ни один из вариантов ksh на основе pdksh -не поддерживает ее, поэтому методы, упомянутые выше для старых версий bash
, не будут работать ни в одной версии ksh. (кроме режима эмуляции ksh zsh ).
В оболочках на основе ksh88, ksh93 и pdksh -один из вариантов — опубликовать -обработку вывода find
, чтобы он сгенерировал код оболочки, который может определить этот массив и оценить этот код с помощьюeval
:
eval set -A F_ARR "$(
LC_ALL=C find "$CDIR" ! -type d -exec awk -v q="'" -- '
BEGIN {
for (i = 1; i < ARGC; i++) {
gsub(q, q "\\" q q, ARGV[i])
printf " %s", q ARGV[i] q
}
exit
}' {} +
)"
(здесь предполагается, что $CDIR
не начинается с +
или -
. Если это возможно, с некоторыми реализациями ksh
вы можете использовать set -A F_ARR --
. В любом случае, если он начинается с -
, find
подавится им ).
Такой же подход можно использовать для любого POSIX sh, заменив set -A F_ARR
на set --
, что затем заполнит один массив оболочек POSIX:"$@"
.
Это ожидаемо — подробности см. на справочной странице . эта цитата All commands of a pipeline are executed in separate subshells
и следующая запись FAQ:
Something is going wrong with my while...read loop
Most likely, you've encountered the problem in which the shell runs all
parts of a pipeline as subshell. The inner loop will be executed in a
subshell and variable changes cannot be propagated if run in a pipeline:
bar | baz | while read foo; do...; done
Note that exit in the inner loop will only exit the subshell and not the
original shell. Likewise, if the code is inside a function, return in
the inner loop will only exit the subshell and won't terminate the func‐
tion.
Use co-processes instead:
bar | baz |&
while read -p foo; do...; done
exec 3>&p; exec 3>&-
If read is run in a loop such as while read foo; do...; done then lead‐
ing whitespace will be removed (IFS) and backslashes processed. You
might want to use while IFS= read -r foo; do...; done for pristine I/O.
Similarly, when using the -a option, use of the -r option might be pru‐
dent (“read -raN-1 arr <file”); the same applies for NUL-terminated
lines:
find. -type f -print0 |& \
while IFS= read -d '' -pr filename; do
print -r -- "found <${filename#./}>"
done
Итак, давайте преобразуем ваш код (также заменили первые две строки на одну, которая правильно очищает массив):
set -A F_ARR
find "$CDIR" ! -type d -mtime +7 -print0 |&
while IFS= read -d '' -pr pathname; do
F_ARR+=("$pathname")
done
echo ${#F_ARR[@]}
Для этого требуется find (1 ), который поддерживает параметр -print0
, и достаточно свежий mksh
на стороне выполнения для поддержки -d ''
для строк с завершением -NUL. Если ваша оболочка Korn более старая, сделайте это без (, но тогда она также не будет поддерживать +=(…)
, так что, скорее всего, она достаточно новая ).
Полное раскрытие :Я mksh разработчик.
Я немного подумал и решил, что в основном сохраняю свой способ кодирования
я не знаком с ksh, поэтому мне приходится кодировать в bash
извините, если вам нужно перевести
нужен временный файл
потому что for
и while
оба порождают оболочку sub -в конвейерах, поэтому не могут передавать значения массива
вы можете изменить шаблон файла, чтобы mktemp
поместить временный файл в каталог, который вам не нужно будет обрабатывать
замените часть эха в цикле while на ваш код
#!/bin/bash
i=0
tmp=`mktemp /tmp/findingXXXXXX`
find "$CDIR" ! -type d -mtime +7 |awk '{print "F_ARR["(NR-1)"]="$0}' >$tmp
. $tmp
rm -f $tmp
while [ "${F_ARR[$i]}" != "" ];
do
echo i=$i ${F_ARR[$i]}
((i++))
done