Ответы:
В Bash вы можете сделать это, включив extglobопцию, как это (заменить lsс cpи добавить целевой каталог, конечно)
~/foobar> shopt extglob
extglob off
~/foobar> ls
abar afoo bbar bfoo
~/foobar> ls !(b*)
-bash: !: event not found
~/foobar> shopt -s extglob # Enables extglob
~/foobar> ls !(b*)
abar afoo
~/foobar> ls !(a*)
bbar bfoo
~/foobar> ls !(*foo)
abar bbar
Позже вы можете отключить Extglob с
shopt -u extglob
f, кроме foo?
Опция extglobоболочки дает вам более мощное сопоставление с образцом в командной строке.
Вы включаете его с помощью shopt -s extglob, а выключаете его с помощью shopt -u extglob.
В вашем примере вы бы изначально сделали:
$ shopt -s extglob
$ cp !(*Music*) /target_directory
Полный доступен внутр закончился GLOB операторы Бинг являются (выдержка из man bash):
Если опция оболочки extglob включена с помощью встроенной функции shopt, распознаются несколько расширенных операторов сопоставления с образцом. Список шаблонов - это список из одного или нескольких шаблонов, разделенных знаком |. Составные шаблоны могут быть сформированы с использованием одного или нескольких из следующих подшаблонов:
- ? (pattern-list)
Соответствует нулю или одному вхождению данных паттернов- * (pattern-list)
Соответствует нулю или большему количеству вхождений данных паттернов- + (pattern-list)
Соответствует одному или нескольким вхождениям заданных шаблонов- @ (pattern-list)
Соответствует одному из заданных шаблонов- ! (pattern-list)
Соответствует всему, кроме одного из заданных шаблонов
Так, например, если вы хотите , чтобы получить список всех файлов в текущем каталоге, которые не являются .cили .hфайлы, вы должны сделать следующее :
$ ls -d !(*@(.c|.h))
Конечно, нормальное сглаживание оболочки работает, поэтому последний пример также можно записать так:
$ ls -d !(*.[ch])
.cили .hявляется каталогом.
D, но не содержимое подкаталогов, которые могут содержаться в каталоге D.
Не в bash (что я знаю), но:
cp `ls | grep -v Music` /target_directory
Я знаю, что это не совсем то, что вы искали, но это решит ваш пример.
Если вы хотите избежать затрат на использование команды exec, я думаю, вы можете добиться большего успеха с помощью xargs. Я думаю, что следующее является более эффективной альтернативой
find foo -type f ! -name '*Music*' -exec cp {} bar \; # new proc for each exec
find . -maxdepth 1 -name '*Music*' -prune -o -print0 | xargs -0 -i cp {} dest/
В bash альтернативой shopt -s extglobявляется GLOBIGNOREпеременная . Это не совсем лучше, но мне легче запомнить.
Пример, который может быть тем, что хотел оригинальный плакат:
GLOBIGNORE="*techno*"; cp *Music* /only_good_music/
Когда это unset GLOBIGNOREбудет сделано, чтобы иметь возможность rm *techno*в исходном каталоге.
Вы также можете использовать довольно простой forцикл:
for f in `find . -not -name "*Music*"`
do
cp $f /target/dir
done
-maxdepth 1для нерекурсивных?
findобратных галочек будет нарушать неприятные способы, если он найдет какие-либо нетривиальные имена файлов.
Мое личное предпочтение - использовать команду grep и while. Это позволяет писать мощные, но читаемые сценарии, гарантирующие, что вы в конечном итоге будете делать именно то, что вам нужно. Кроме того, с помощью команды echo вы можете выполнить пробный прогон перед выполнением фактической операции. Например:
ls | grep -v "Music" | while read filename
do
echo $filename
done
распечатает файлы, которые вы в конечном итоге скопировать. Если список правильный, следующий шаг - просто заменить команду echo командой copy следующим образом:
ls | grep -v "Music" | while read filename
do
cp "$filename" /target_directory
done
bashвы можете использовать while IFS='' read -r filename, но тогда новые строки по - прежнему является проблемой. В общем, лучше не использовать lsдля перечисления файлов; инструменты, как findгораздо лучше подходят.
for file in *; do case ${file} in (*Music*) ;; (*) cp "${file}" /target_directory ; echo ;; esac; done
Уловка, которую я еще не видел здесь, которая не использует extglob, findили grepсостоит в том, чтобы рассматривать два списка файлов как наборы и «различать» их, используя comm:
comm -23 <(ls) <(ls *Music*)
commпредпочтительнее, diffпотому что у него нет лишних хлопот.
Это возвращает все элементы множества 1, ls, которые не также в наборе 2 ls *Music*. Это требует, чтобы оба набора были в отсортированном порядке для правильной работы. Нет проблем для lsрасширения и glob, но если вы используете что-то подобное find, обязательно вызовите sort.
comm -23 <(find . | sort) <(find . | grep -i '.jpg' | sort)
Потенциально полезно.
comm -23 <(ls) exclude_these.list
Одно решение для этого можно найти с помощью find.
$ mkdir foo bar
$ touch foo/a.txt foo/Music.txt
$ find foo -type f ! -name '*Music*' -exec cp {} bar \;
$ ls bar
a.txt
Поиск имеет довольно много опций, вы можете получить довольно конкретные сведения о том, что вы включаете и исключаете.
Изменить: Адам в комментариях отметил, что это рекурсивно. параметры поиска mindepth и maxdepth могут быть полезны для управления этим.
find -maxdepth 1 -not -name '*Music*'подстановочного
В следующих работах перечислены все *.txtфайлы в текущем каталоге, кроме тех, которые начинаются с цифры.
Это работает bash, dash, zshи все другие POSIX совместимых оболочек.
for FILE in /some/dir/*.txt; do # for each *.txt file
case "${FILE##*/}" in # if file basename...
[0-9]*) continue ;; # starts with digit: skip
esac
## otherwise, do stuff with $FILE here
done
В первой строке шаблон /some/dir/*.txtзаставит forцикл перебирать все файлы /some/dir, имена которых заканчиваются на .txt.
Во второй строке оператор case используется для отсеивания нежелательных файлов. - ${FILE##*/}Выражение удаляет любой начальный компонент имени dir из имени файла (здесь /some/dir/), чтобы шаблоны могли сопоставлять только базовое имя файла. (Если вы только отсеиваете имена файлов на основе суффиксов, вы можете сократить это до $FILE.)
В третьей строке все файлы, соответствующие caseшаблону [0-9]*), будут пропущены ( continueоператор переходит к следующей итерации forцикла). - Если вы хотите, вы можете сделать что-то более интересное здесь, например, пропустить все файлы, которые не начинаются с буквы (a – z) [!a-z]*, или вы можете использовать несколько шаблонов, чтобы пропустить несколько видов имен файлов, например, [0-9]*|*.bakчтобы пропустить файлы обоих .bakфайлов. и файлы, которые не начинаются с цифры.
*.txtвместо просто *). Исправлено сейчас.
это сделало бы это, исключая именно «музыку»
cp -a ^'Music' /target
это и это для исключения таких вещей, как музыка? * или *? музыка
cp -a ^\*?'complete' /target
cp -a ^'complete'?\* /target
cpруководства по MacOS имеет -aопцию, но она делает что-то совершенно другое. Какая платформа поддерживает это?
ls /dir/*/!(base*)