LINUX.ORG.RU

cp не копирует скрытые файлы с аргументом !

 , ,


0

1

Нужно скопировать /home/user со всем содержимым, но без /home/user/Desktop. Ввожу cp -prv /home/user/!(Desktop), Desktop не копирует, а вместе с ним не копирует всё, что начинается с точки в папке user. cp -prv /home/user/.!(Desktop) не копирует Desktop, но копирует только скрытые файлы. Что делать? Можно, конечно, использовать обе команды по очереди, но блин!..

Ответ на: комментарий от avgust23

Шашечки или ехать? Чем rsync не устроил?

Вообще твоя проблема не а cp, а в шелле (bash или что там у тебя). Сам cp понятия не имеет ни о каком !(Desktop), он получает список уже отрезолвленный шеллом.

Можно, конечно, использовать обе команды по очереди, но блин!

Зачем по очереди? У одной используй оба аргумента. Они ж в списки аргументов раскрываются.

cp -prv /home/user/!(Desktop) /home/user/.* destination/

CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 2)

Можно, конечно, использовать обе команды по очереди

А можно и одновременно.

cp source1 source2 ... sourceN destination/

cp -prv /home/user/.!(Desktop) не копирует Desktop, но копирует только скрытые файлы

а в этом случае ничего и не потребуется исключать. Desktop и так не начинается с точки

router ★★★★★
()
Последнее исправление: router (всего исправлений: 1)
Ответ на: комментарий от avgust23

tar, rsync… Хотелось бы цпшкой.

Тем не менее, оба эти варианта лучше

Посмотри в ABS как копировать с помощью tar через конвеер, без создания промежуточного файла с архивом

router ★★★★★
()
Ответ на: комментарий от avgust23

Нет никаких ограничений — просто в cp нет такой фичи что-то там исключать. У rsync есть, да ещё и копирует кучу мелких файлов он намного быстрее. Как это сделать с помощью cp (ну в теории может быть полезно там, где нет rsync, но при этом шелл таки понимает такие продвинутые глобы…) я тоже написал.

CrX ★★★★★
()
Ответ на: комментарий от CrX

Есть конечно, но не в этом случае.

$: bash -c 'echo *'                      
Desktop Downloads Документы Загрузки Картинки Документ-2025-06-30-205228.pdf

$: bash -c 'echo /home/alien/.!(Desktop)'
bash: -c: строка 1: синтаксическая ошибка рядом с неожиданным маркером «(»
bash: -c: строка 1: `echo /home/alien/.!(Desktop)'

$: bash -c 'echo /home/alien/.!(Desktop)*'
bash: -c: строка 1: синтаксическая ошибка рядом с неожиданным маркером «(»
bash: -c: строка 1: `echo /home/alien/.!(Desktop)*'
ALiEN175
()
Ответ на: комментарий от ALiEN175

Не знаю, почему не работает через bash -c, но в man bash это есть.

upd: понял. В баше тоже по умолчанию не работает, надо сперва ещё и shopt -s extglob сделать. Тогда работает.


% /tmp/test % ./1.sh   
./1.sh ./2.sh ./abc ./dfg ./tyy ./uorwfg

% 21:36:40 /tmp/test % ./2.sh   
./2.sh: line 2: syntax error near unexpected token `('
./2.sh: line 2: `echo ./!(asd)'

% cat ./1.sh
#!/bin/bash
shopt -s extglob
echo ./!(asd)

% cat ./2.sh 
#!/bin/bash
echo ./!(asd)

CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 4)
Ответ на: комментарий от CrX

надо сперва ещё и shopt -s extglob сделать

Есть shopt -s dotglob, который включает ровно то, что нужно ТС — * начинает матчить и имена начинающиеся на точку.

i-rinat ★★★★★
()

Искать файлы по определенному условию проще через find, а потом уже через pipe и xargs выполнять с ними другую более простую команду типа cp. В случае find есть опция -exec, поэтому xargs не нужен:

find . -maxdepth 1 -mindepth 1 -not -name "Desktop" -exec cp -pPrx '{}' /foo/ \;
MirandaUser2
()
Ответ на: комментарий от kaldeon

Почему не tar и не rsync: привычного /bin/cp должно хватать для простых операций.

Почему не find: потому что обработка текста — это крайне удобная и подходящая абстракция, а find — специальный API.

Почему меняем IFS: дефолтный IFS предназначен для обработки случая «файлы между пробелами». Этот случай не выдержал проверку времени, ибо сегодня намного чаще «одна строка = один файл».

Почему забиваем хер на переводы строк в имени файла: ограничение выбранной абстракции (обработка строк).

kaldeon
()
Последнее исправление: kaldeon (всего исправлений: 2)
Ответ на: комментарий от kaldeon

Если не умеете корректно обрабатывать пробелы c табуляцией - тогда, конечно будет «удобно». В кавычках.

IFS='
' # newline

И еще вот этот ваш вырвиглаз. Не знаете, как в переменную перенос строки записать? Пользуйтесь - IFS=$'\n'

ALiEN175
()
Последнее исправление: ALiEN175 (всего исправлений: 1)
Ответ на: комментарий от ALiEN175

Если не умеете корректно обрабатывать пробелы c табуляцией - тогда, конечно будет «удобно».

Не понял. Можно пример?

Не знаете, как в переменную перенос строки записать?

Знаем. Вот пример: "
". В кавычках перенос строки. Именно так он и пишется практически везде, с возникновения письменности до наших времён. Пользуйтесь.

kaldeon
()
Последнее исправление: kaldeon (всего исправлений: 2)
Ответ на: комментарий от ALiEN175

А, типа *.c учитывает пробелы. Это да. Но мне-то нужно убрать определённые элементы. И тут много вариантов, даже цикл неплохо подходит (лучше чем tar/rsync/find), но я выбрал обратку текста grep’ом. Можно было и по-другому, но раз уж так, то выхлоп как-то нужно перевести в список аргументов и IFS ровно для этой задачи и предназначен.

kaldeon
()
Последнее исправление: kaldeon (всего исправлений: 1)
Ответ на: комментарий от ALiEN175

Тогда wildcards, find или даже python. Но это если такие файлы есть. У меня вот нет таких файлов.

Обвинять grep в порче переводов строк — это всё равно что обвинять регулярные выражения в том, что они не могут парсить таблицы. Но никто же не предлагает из-за этого переходить на bnf во всех инструментах.

kaldeon
()
Последнее исправление: kaldeon (всего исправлений: 1)
Ответ на: комментарий от ALiEN175

А как ещё? Ну допустим grep выхлопнул

hello world.txt
existence.txt

Два файла. А со стандартным IFS мы получим три.

Можно, конечно, while read и массивы или readarray, но это излишне в сравнении с $().

kaldeon
()
Последнее исправление: kaldeon (всего исправлений: 3)
Ответ на: комментарий от ALiEN175

Вариант с IFS лучше, потому что позволяет решить проблему на уровне языка. Это проявляется, например, в том, что я могу более естественно написать cp src dst, а не cp -t dst src. В openbsd, например, нет опции -t.

xargs был бы допустим для обработки нулевого разделителя.

kaldeon
()
Последнее исправление: kaldeon (всего исправлений: 1)
Ответ на: комментарий от kaldeon

for F in ./*; do [[ ! $F =~ Desktop ]] && cp "$F" /dest; done

Такая штука скопирует файлы с абсолютно любым непотребстром в именах, в том числе и c пробелами, и с кавычками, и с переносом строк. Нет грепа, нет труб. И IFS я тут вообще не трогаю. Чистый баш (насчёт [[ не уверен) и утилита cp.

ALiEN175
()
Последнее исправление: ALiEN175 (всего исправлений: 4)
Ответ на: комментарий от ALiEN175

cp $(cat) /tmp короче и прямолинейнее выглядит, чем xargs -d\n -i{} cp {} /tmp. Мой вариант даже в специальном форматировании не нуждается. Это потому что «IFS позволяет решить проблему на уровне языка».

Недавно в соседнем треде обсуждалось всё то же самое. Вот мой вариант, написанный на телефоне без проверки, но работающий. А вот так получается, если следовать советам про «текст не трогать, всё через \0, ресурсы экономить» (я замучался его писать). Выглядит ужасно и я не уверен, что его можно упростить. Больше никто работающий пример не скинул.

Возможность легко наложить обработку текста на примитивы операционной системы (файлы) — в этом вся суть шелла. Это duck typing, если нужно умное слово. Вместо отдельного API для работы с файлами мы ограничиваемся работой с текстом и покрываем 90% потребностей. Это непривычный подход, но таков шелл. Если избавиться от этого аспекта, то это будет язык хуже фортрана.

kaldeon
()
Ответ на: комментарий от kaldeon

cp $(cat) /tmp

У меня не работает.

$: ls                                          
'somebad'$'\r\n'  'with'$'\n''newline'  'with '\''quotes"'  'with space'

$: bash -c 'IFS=$'\n' ls | cp $(cat) /tmp'
cp: не удалось выполнить stat для 'somebad'$'\r': Нет такого файла или каталога
cp: не удалось выполнить stat для 'with': Нет такого файла или каталога
cp: не удалось выполнить stat для 'newline': Нет такого файла или каталога
cp: не удалось выполнить stat для 'with': Нет такого файла или каталога
cp: не удалось выполнить stat для ''\''quotes"': Нет такого файла или каталога
cp: не удалось выполнить stat для 'with': Нет такого файла или каталога
cp: не удалось выполнить stat для 'space': Нет такого файла или каталога

$: bash --version
GNU bash, версия 5.3.0(1)-release (x86_64-pc-linux-gnu)
ALiEN175
()
Ответ на: комментарий от ALiEN175

Это тоже хороший вариант. Вообще ничего плохого про него не хочется говорить. Хотя синтаксис самого баша слегка подкачивает, можно было бы сделать лучше.

В rc тоже самое делается так:

for (f in *) if (! ~ $f Desktop) cp $f /dest

Нет грепа, нет труб. И IFS я тут вообще не трогаю.

Чтобы что?

Чистый баш

Трубы (трубы особенно) и IFS — это и есть чистый баш. Избегать grep странно, это главный мультитул баша.

kaldeon
()
Ответ на: комментарий от kaldeon

for (f in *) if (! ~ $f Desktop) cp $f /dest

Не нужно ЛИЧНО ВАШ натвиканный баш тут представлять.

$: bash -c 'for (f in *) if (! ~ $f Desktop) cp $f /dest'
bash: -c: строка 1: синтаксическая ошибка рядом с неожиданным маркером «(»
bash: -c: строка 1: `for (f in *) if (! ~ $f Desktop) cp $f /dest'
ALiEN175
()
Ответ на: комментарий от ALiEN175

Потому что используешь неправильно. Оно только на \n будет спотыкаться. Но я уже комментировал это неоднократно. Можно ещё добавить, что с \n, скорее всего, любой системный шелловский скрипт сломается.

kaldeon
()
Ответ на: комментарий от ALiEN175

Выше команду привёл. Ничего не спотыкается.

Команда хорошая. Плохо, что вы отказываетесь от встроенных эффективных средств ради того, чтобы покрыть 0.1% случаев, причём чисто из-за спора со мной, а не реально возникшей ситуации.

Почитай на досуге

Я прочёл весь ман 10 лет назад. Если этого недостаточно, то предложить большее не могу.

kaldeon
()
Последнее исправление: kaldeon (всего исправлений: 2)
Ответ на: комментарий от kaldeon

Плохо, что вы отказываетесь от встроенных эффективных средств

Насильничать IFS это плохо. И так и не увидел внятного ответа, что делать, если перенос строки окажется в имени файла. И оставьте банальщину - «переименовать ручками».

причём чисто из-за спора со мной, а не реально возникшей ситуации.

В споре рождается истина. Я за хорошие аргументы) Но у вас плохо получается.

Я прочёл весь ман 10 лет назад.

У меня примерно так же. Но всё меняется. Вон, недавно даже не смог заставить ls покромсать имена файлов(

ALiEN175
()
Последнее исправление: ALiEN175 (всего исправлений: 1)
Ответ на: комментарий от ALiEN175

Насильничать IFS это плохо.

Чем плохо? Благодаря тому, что я ставлю в него более адекватное значение, у меня получилось самое простое, короткое и прямолинейное решение за весь тред.

kaldeon
()
Последнее исправление: kaldeon (всего исправлений: 1)