Поскольку вы используете SLES, вы можете использовать расширения GNU, чтобы сделать это безопаснее:
find trainB -mindepth 1 -maxdepth 1 ! -name '.*' -print0 |
shuf -n 5533 -z |
xargs -r0 mv -t testB
Это использует find
для обработки списков файлов через каналы вместо аргументов строки команды -, затем перемешивает их, ограничивая вывод, и, наконец, перемещает их в testB
. Параметры -print0
, -z
и -0
обеспечивают использование нулевых терминаторов вместо символов новой строки.
Вместо find
можно использовать:
printf '%s\0' trainB/*
printf
встроен -в bash
, на него не влияет слишком длинный список аргументов ограничение системного вызова execve()
. Однако это потенциально менее эффективно, так как оболочке необходимо построить весь список и отсортировать его, в то время как find
отображает пути к файлам без сортировки по мере их поступления.
На самом деле это не кажется слишком простым. Вероятно, было бы лучше, если бы оболочка поддерживала ожидание замены процесса, но я не думаю, что Bash делает это.
Другая проблема заключается в том, что именованный канал не может знать, сколько строк вы собираетесь туда записать. Канал будет считывать EOF, когда все модули записи закрыты, но вы, скорее всего, получите по одному для каждой echo
записи. Если только они не попали в одно и то же время, в этом случае вы этого не сделаете.
Но представляется возможным организовать подстановки процессов так, чтобы запись fd была открыта с самого начала, чтобы EOF появлялся только один раз после того, как все они завершены.
Что-то вроде этого, где echo somecommand
, true
и false
обозначают фактические команды:
#!/bin/bash
dir=$(mktemp -d)
p="$dir/p"
mkfifo "$p"
# whole subshell sent to the background
(exec 3> "$p";
# both process substitutions get a copy of fd 3
echo somecommand \
<(false; echo "cmd1: $?" >&3) \
<(true; echo "cmd2: $?" >&3) \
) &
# read the exit statuses, this will see EOF once all the three
# background processes above finish
cat "$p"
rm -rf "$dir" 2>/dev/null
Обратите внимание, что порядок строк, выводимых в конвейер, зависит от времени и по существу является случайным.
Кроме того, если echo somecommand
работает медленно, вывод из cat "$p"
может появиться первым. Вам нужно будет прочитать данные из канала в переменную, а затем wait
для фонового процесса.
Также есть возможность без фоновой обработки somecommand
, но для этого нужна дополнительная гимнастика с файловыми дескрипторами:
#!/bin/bash
dir=$(mktemp -d)
p="$dir/p"
mkfifo "$p"
# open an fd for read+write (doesn't block because both open)
exec 3<>"$p"
# process substs inherit the fd, closing it when they exit
echo somecommand \
<(false; echo "cmd1: $?" >&3) \
<(true; echo "cmd2: $?" >&3) \
# open another reader to keep the pipe live
exec 4<"$p"
# now we can close the writing handle
exec 3>&-
# read the data off
cat <&4
exec 4<&-
rm -rf "$dir" 2>/dev/null
Может быть проще просто собрать статусы выхода в обычный файл и читать его, пока не появится известное количество строк.
#!/bin/bash
f=$(mktemp)
# number of process substitutions
n=2
echo somecommand \
<(false; echo "cmd1: $?" >>"$f") \
<(true; echo "cmd2: $?" >>"$f") \
exec 3< "$f"
# read that many lines
for ((i = 0; i < n; i++)) do
# if the data isn't there yet, retry reading until a new line appears
until read line <&3; do sleep 1; done
echo "$line";
done
exec 3<&-
rm -f "$f"
Насколько я проверял, все три работают, но работа с подстановками процессов и конвейерами может быть достаточно сложной, поэтому я мог упустить какой-то режим отказа.