2008-10-19 5 views
263

Скажем, я хочу скопировать содержимое каталога, исключая файлы и папки, имена которых содержат слово «Музыка».Как использовать обратные или отрицательные подстановочные знаки при сопоставлении шаблонов в оболочке unix/linux?

cp [exclude-matches] *Music* /target_directory 

Что следует делать вместо [исключить совпадения], чтобы выполнить это?

ответ

304

В Bash вы можете сделать это, включив опцию extglob, как это (заменить Ls для ф и добавить целевой каталог, конечно)

~/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 
4

Одно решение для этого можно найти с помощью find.

$ mkdir foo bar 
$ touch foo/a.txt foo/Music.txt 
$ find foo -type f ! -name '*Music*' -exec cp {} bar \; 
$ ls bar 
a.txt 

Найти есть несколько вариантов, вы можете получить довольно конкретную информацию о том, что вы включаете и исключаете.

Редактировать: Adam в комментариях отметил, что это рекурсивный. найти варианты mindepth и maxdepth могут быть полезны в управлении этим.

+0

Это рекурсивная копия, которая отличается поведением. Он также генерирует новый процесс для каждого файла, который может быть очень неэффективным для большого количества файлов. – 2008-10-19 21:25:47

+0

Стоимость нереста процесса равна нулю по сравнению со всеми IO, которые генерируют копирование каждого файла. Поэтому я бы сказал, что это достаточно хорошо для случайного использования. – dland 2008-10-19 21:29:23

+0

Некоторые обходные пути для процесса нереста: http://stackoverflow.com/questions/186099/how-do-you-handle-the-too-many-files-problem-when-working-in-bash – 2008-10-19 21:34:18

18

Не в Баш (что я знаю), но:

cp `ls | grep -v Music` /target_directory 

Я знаю, что это не совсем то, что вы искали, но это будет решить пример.

+0

По умолчанию ls будет помещать несколько файлов в строку, что, вероятно, не даст правильных результатов. – 2008-10-19 21:29:29

+8

Только когда stdout является терминалом. При использовании в конвейере ls печатает одно имя файла на строку. – 2008-10-19 21:31:30

+0

ls помещает только несколько файлов в строку при выводе на терминал. Попробуйте сами - «ls | less» никогда не будет иметь несколько файлов в строке. – SpoonMeiser 2008-10-19 21:32:34

5

Вы также можете использовать довольно простой for цикл:

for f in `find . -not -name "*Music*"` 
do 
    cp $f /target/dir 
done 
9

Если вы хотите AVO id mem-стоимости использования команды 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/ 
186

Опция extglob оболочки дает более мощный поиск по шаблону в командной строке.

Включите его с помощью shopt -s extglob и выключите его с помощью shopt -u extglob.

В вашем примере, вы бы сначала сделать:

$ shopt -s extglob 
$ cp !(*Music*) /target_directory 

Полный доступный внутр закончился Глоб операторы Бинг являются (выдержка из man bash):

Если опция extglob оболочки разрешено с использованием встроенного магазина, признано несколько расширенных операторов сопоставления шаблонов. В следующем описании, pat tern-list представляет собой список из одного или нескольких шаблонов, разделенных символом |. Композитные структуры могут быть сформированы с использованием одного или более из следующих подшаблонов:

  • ?(Шаблон)
    соответствует нулю или одно вхождение указанных шаблонов
  • * (шаблон)
    Соответствует нулю или более вхождений указанных шаблонов
  • + (шаблон)
    Соответствует одному или более вхождений указанных шаблонов
  • @ (шаблон)
    Матчи один из заданных шаблонов
  • ! (Шаблон)
    Матчи ничего, кроме одного из указанных шаблонов

Так, например, если вы хотите, чтобы получить список всех файлов в текущем каталоге, которые не .c или .h файлы, вы могли бы сделать:

$ ls -d !(*@(.c|.h)) 

конечно, нормальные оболочки globing работы, поэтому последний пример можно записать в виде:

$ ls -d !(*.[ch]) 
0

это будет делать это за исключением точно 'Music'

cp -a ^'Music' /target 

это и что за исключением вещей, как музыка? * Или *? Музыка

cp -a ^\*?'complete' /target 
cp -a ^'complete'?\* /target 
4

Мое личное предпочтение использовать Grep и команда while. Это позволяет писать мощные, но читаемые сценарии, гарантирующие, что вы в конечном итоге выполняете именно то, что хотите. Кроме того, используя команду эхо-сигнала, вы можете выполнить сухой прогон перед выполнением фактической операции. Например:

ls | grep -v "Music" | while read filename 
do 
echo $filename 
done 

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

ls | grep -v "Music" | while read filename 
do 
cp "$filename" /target_directory 
done 
2

Ниже перечислены все работает *.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 
  1. В первой линии узора /some/dir/*.txt вызовет цикл for перебрать все файлы в /some/dir, чье имя заканчивается .txt.

  2. В строке два аргумент case используется для отсечения нежелательных файлов. - Выражение ${FILE##*/} отбрасывает любой ведущий компонент имени dir из имени файла (здесь /some/dir/), так что паттерны могут соответствовать только базовому имени файла. (Если вы только отбираете имена файлов на основе суффиксов, вы можете сократить это значение до $FILE.)

  3. В третьей строке, все файлы, соответствующие [0-9]*) линий case шаблона будут пропущены (continue оператор переходит к следующей итерации цикла for). - Если вы хотите, чтобы вы могли сделать что-то более интересное здесь, например. например, пропустить все файлы, которые не начинаются с буквы (a-z) с использованием [!a-z]*, или вы можете использовать несколько шаблонов для пропуска нескольких типов имен файлов, например. [0-9]*|*.bak, чтобы пропустить файлы и файлы .bak и файлы, которые не начинаются с числа.

3

В Баш, альтернативой shopt -s extglob является GLOBIGNORE variable. Это не лучше, но мне легче запомнить.

Пример, который может быть то, что оригинальный плакат хотел:

GLOBIGNORE="*techno*"; cp *Music* /only_good_music/ 

Когда это сделано, unset GLOBIGNORE, чтобы иметь возможность rm *techno* в исходном каталоге.

2

Уловка я не видел здесь еще, что не использует 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) 

Потенциально полезный.