6

У меня есть каталог с пронумерованными файлами, например, 1_foo.txt 2_bar.asc 13_test.png, и я хочу переместить их в отдельные каталоги (например, 1, 2 и 13), используя как можно более простую команду bash.

Создать нумерованные каталоги было легко:

mkdir $(seq 1 15)

Я также придумал команду для копирования файлов в соответствующие каталоги:

seq 15 -1 1 | xargs -I@ mv @_* @

Однако это не работает, так как * интерпретируется как нормальный символ при использовании с xargs, что приводит к таким ошибкам, как «mv: File '15_ *' not found».

Есть ли простой способ использовать * в качестве символа подстановки в команде, вызываемой xargs?

2 ответа2

17

Вы были очень близки

seq 15 -1 1 | xargs -I@ sh -c 'mv @_* @'

Вам нужно отложить интерпретацию (расширение) * до тех пор, пока не произойдет замена @ .  (Но вы уже поняли, что это была проблема, верно?)

Мне посоветовали никогда не вставлять неизвестное имя файла (или другую строку подстановки) непосредственно в командную строку оболочки.  Приведенный выше пример, вероятно, довольно безопасен, потому что вы знаете, какими будут строки - 15 , 14 ,…, 3 , 2 и 1 .  Но использование приведенного выше примера в качестве шаблона для более сложных команд может быть опасным.  Более безопасное соглашение будет

seq 15 -1 1 | xargs -I@ sh -c 'mv "$1"_* "$1"' x-sh @

где x-sh - произвольная строка, которая будет использоваться для обозначения любых сообщений об ошибках, генерируемых вызванной оболочкой.  Это эквивалентно моему первому примеру, за исключением того, что вместо встраивания строк (представленных @) непосредственно в команду оболочки, он внедряет их, предоставляя @ в качестве аргумента для оболочки, а затем ссылается на них как "$1" .


PS Вы предложили запустить команду seq в обратном порядке (seq 15 -1 1 , которая генерирует 15 , 14 ,…, 3 , 2 , 1 а не 1 , 2 , 3 ,…, 14 , 15), и никто не упомянул об этом.  Это было бы важной частью ответа, если бы ваши имена файлов были такими, как 1foo.txt , 2bar.asc , 13test.png и т.д. (С различными символами, появляющимися после числа, а не всегда _).  В этом случае команда будет mv "$1"* "$1" (без _), и, если вы сначала сделали 1 , то команда mv 1* 1 сместит все 10quick* , 11brown* , 12fox* и т. д., файлы вместе с 1foo* .  Но

seq 1 15 | xargs -I@ sh -c 'mv "$1"_* "$1"' x-sh @

должен быть в безопасности.

PPS Команда seq не указана в POSIX. Также нет расширения скобки в оболочке.  POSIX-совместимое решение может быть построено, комбинируя ответ Гравити на этот вопрос с другим ответом Адама Каца:

i=1
while [ "$i" -le 15 ]
do
    mv "${i}"_* "$i"
    i=$((i+1))
done
6

Только не используйте xargs для этого. Используйте цикл for :

for i in $(seq 1 15); do
    mv ${i}_* $i
done

Еще лучше использовать расширение скобок вместо seq:

mkdir {1..15}

for i in {1..15}; do
    mv ${i}_* $i
done

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .