Невозможно присвоить значения массиву с помощью команды -exec результата поиска

На основеhttps://stackoverflow.com/a/2957781/53897:

echo MQSeriesRuntime_5-U200491-7.5.0-4.x86_64 | perl -n -e '/_([^-]+)/ && print $1'

0
19.09.2019, 20:18
3 ответа
find... -exec sh -c 'sh code' {} +

В оболочке bash/ksh запускает команду findв новом процессе, который запускает один или несколько вызовов команд sh. sh codeсможет влиять только на переменные вызова sh, который интерпретирует этот код (, кстати, shне предназначен для поддержки массивов ).

Если вы хотите сохранить пути к файлам, найденным с помощью find, в массив текущей оболочки, именно эта оболочка должна выполнить назначение.

С bash4.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^/))

Отличие от вышеизложенного заключается в том, что:

  • список файлов отсортирован, вы можете добавить квалификатор oNglob, чтобы отключить сортировку
  • $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:"$@".

4
28.04.2021, 23:29

Это ожидаемо — подробности см. на справочной странице . эта цитата 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 разработчик.

1
28.04.2021, 23:29

Я немного подумал и решил, что в основном сохраняю свой способ кодирования
я не знаком с 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

0
28.04.2021, 23:29

Теги

Похожие вопросы