У меня есть много файлов, которые упорядочены по имени файла в каталоге. Я хочу скопировать последние N (скажем, N = 4) файлов в мой домашний каталог. Как я должен это делать?
Во всех ответах ниже вам может потребоваться добавить «LC_ALL = ...» перед командами, использующими диапазоны, чтобы их диапазоны использовали правильную локаль для вас (локали могут измениться, а затем порядок символов может измениться). Например, в некоторых локалях [az] может содержать все буквы алфавита, кроме «Z», поскольку буквы упорядочены: aAbBcC .... zZ. Существуют другие варианты (акцентированные буквы и даже более экзотические порядки Обычный выбор:LC_ALL=C
Оливье Дюлак
Ответы:
23
Это легко сделать с помощью массивов bash / ksh93 / zsh:
a=(*)
cp --"${a[@]: -4}"~/
Это работает для всех не скрытых имен файлов, даже если они содержат пробелы, символы табуляции, новые строки или другие сложные символы (при условии, что в текущем каталоге есть как минимум 4 не скрытых файла с bash ).
Как это устроено
a=(*)
Это создает массив aсо всеми именами файлов. Имена файлов, возвращаемые bash, сортируются в алфавитном порядке. (Я предполагаю, что это то, что вы подразумеваете под «упорядоченным по имени файла». )
${a[@]: -4}
Это возвращает последние четыре элемента массива a(при условии, что массив содержит как минимум 4 элемента с bash).
cp -- "${a[@]: -4}" ~/
Это копирует последние четыре имени файла в ваш домашний каталог.
Копировать и переименовывать одновременно
Это скопирует последние четыре файла только в домашний каталог и в то же время добавит строку a_к имени скопированного файла:
Скопируйте из другого каталога, а также переименуйте
Если мы используем a=(./some_dir/*)вместо a=(*), тогда у нас есть проблема присоединения каталога к имени файла. Одним из решений является:
a=(./some_dir/*)for f in"${a[@]: -4}";do cp "$f"~/a_"${f##*/}";done
Другое решение - использовать подоболочку и cdкаталог в подоболочке:
(cd ./some_dir && a=(*)&&for f in"${a[@]: -4}";do cp --"$f"~/a_"$f";done)
После завершения подоболочки оболочка возвращает нас в исходный каталог.
Убедиться, что порядок соответствует
Вопрос просит файлы "упорядоченные по имени файла". Этот порядок, отмечает Оливье Дюлак в комментариях, будет варьироваться от одного региона к другому. Если важно иметь фиксированные результаты, не зависящие от настроек компьютера, то лучше всего указывать локаль явно при определении массива a. Например:
LC_ALL=C a=(*)
Вы можете узнать, в какой локали вы находитесь в данный момент, запустив localeкоманду.
Если вы используете, zshвы можете заключить в скобки ()список так называемых глобальных квалификаторов, которые выбирают нужные файлы. В вашем случае это было бы
cp *(On[1,4])~/
Здесь Onимена файлов сортируются в алфавитном порядке в обратном порядке и [1,4]занимают только первые 4 из них.
Вы можете сделать это более надежным путем выбора только обычные файлы ( за исключением каталогов, трубы и т.д.) с ., а также путем присоединения --к cpкоманде, чтобы к файлам лакомств, имена которых начинаются с -должным образом, так:
cp --*(.On[1,4])~
Добавьте Dклассификатор, если вы также хотите рассмотреть скрытые файлы (dot-файлы):
@ StéphaneChazelas да, давайте уточним для будущих читателей, что сортировка в алфавитном порядке в обычном направлении ( on) является поведением по умолчанию, поэтому указывать это не нужно, и -перед цифрами отсчитывает имена файлов снизу.
Джимми
7
Вот решение с использованием чрезвычайно простых команд bash.
Это очень похоже на bashпредлагаемое решение для конкретного массива, но должно переноситься на любую POSIX-совместимую оболочку. Некоторые заметки об обоих методах:
Важно, чтобы вы приводили свои cpаргументы w / cp --или вы получали одну из .точек или /заголовок каждого имени. Если вам не удастся сделать это, вы рискуете первым аргументом первой -черты в cpначале, который может быть интерпретирован как опция и может привести к сбою операции или к непредвиденным результатам.
Даже если вы работаете с текущим каталогом в качестве исходного каталога, это легко сделать как ... set -- ./*или array=(./*).
При использовании обоих методов важно, чтобы в вашем массиве arg было как минимум столько элементов, сколько вы пытаетесь удалить - я делаю это здесь с математическим расширением. Это shiftпроисходит только в том случае, если в массиве arg есть по крайней мере 4 элемента, а затем удаляются только те первые аргументы, которые дают излишек в 4 ..
Так что в set 1 2 3 4 5случае это сместит 1прочь, но в set 1 2 3случае это ничего не сместит.
Если есть только файлы и их имена не содержат пробелов, $IFSсимволов новой строки (и вы не изменили ) или символов глобуса (или некоторых непечатаемых символов с некоторыми реализациями ls) и не начинаются с ., то вы можете сделать это :
Благодарность! Оно работает. Можно ли быстро добавить префикс a_ко ВСЕМ четырем файлам? Я пытался echo "a_$(ls | tail -n 4)", но это только prepends первый файл.
Sibbs Gambling
@SibbsGambling Мой подход не подходит для этого, но ответ John1024 может быть легко адаптирован к этому.
Хауке Лагинг
5
В общем случае lsвывод синтаксического анализа считается дурным тоном, поскольку обычно он приводит к ужасным ошибкам в именах файлов, которые содержат не только пробелы, но также символы табуляции, новые строки или другие допустимые, но "сложные" символы.
HBruijn
2
@HaukeLaging Даже если в настоящее время это не проблема (потому что у меня нет «сложных» огненных имен в моем каталоге), я мог бы через 2 года, и вдруг все
встало
1
Это простое решение bash без какого-либо цикла кода. Скопируйте последние 4 файла из текущего каталога в место назначения:
Как вы гарантируете, что findотдаете файлы в нужном порядке? Как вы гарантируете, что файлы, содержащие пробельные символы (включая символы новой строки) в их именах, обрабатываются правильно?
LC_ALL=C
Ответы:
Это легко сделать с помощью массивов bash / ksh93 / zsh:
Это работает для всех не скрытых имен файлов, даже если они содержат пробелы, символы табуляции, новые строки или другие сложные символы (при условии, что в текущем каталоге есть как минимум 4 не скрытых файла с
bash
).Как это устроено
a=(*)
Это создает массив
a
со всеми именами файлов. Имена файлов, возвращаемые bash, сортируются в алфавитном порядке. (Я предполагаю, что это то, что вы подразумеваете под «упорядоченным по имени файла». )${a[@]: -4}
Это возвращает последние четыре элемента массива
a
(при условии, что массив содержит как минимум 4 элемента сbash
).cp -- "${a[@]: -4}" ~/
Это копирует последние четыре имени файла в ваш домашний каталог.
Копировать и переименовывать одновременно
Это скопирует последние четыре файла только в домашний каталог и в то же время добавит строку
a_
к имени скопированного файла:Скопируйте из другого каталога, а также переименуйте
Если мы используем
a=(./some_dir/*)
вместоa=(*)
, тогда у нас есть проблема присоединения каталога к имени файла. Одним из решений является:Другое решение - использовать подоболочку и
cd
каталог в подоболочке:После завершения подоболочки оболочка возвращает нас в исходный каталог.
Убедиться, что порядок соответствует
Вопрос просит файлы "упорядоченные по имени файла". Этот порядок, отмечает Оливье Дюлак в комментариях, будет варьироваться от одного региона к другому. Если важно иметь фиксированные результаты, не зависящие от настроек компьютера, то лучше всего указывать локаль явно при определении массива
a
. Например:Вы можете узнать, в какой локали вы находитесь в данный момент, запустив
locale
команду.источник
Если вы используете,
zsh
вы можете заключить в скобки()
список так называемых глобальных квалификаторов, которые выбирают нужные файлы. В вашем случае это было быЗдесь
On
имена файлов сортируются в алфавитном порядке в обратном порядке и[1,4]
занимают только первые 4 из них.Вы можете сделать это более надежным путем выбора только обычные файлы ( за исключением каталогов, трубы и т.д.) с
.
, а также путем присоединения--
кcp
команде, чтобы к файлам лакомств, имена которых начинаются с-
должным образом, так:Добавьте
D
классификатор, если вы также хотите рассмотреть скрытые файлы (dot-файлы):источник
*([-4,-1])
.on
) является поведением по умолчанию, поэтому указывать это не нужно, и-
перед цифрами отсчитывает имена файлов снизу.Вот решение с использованием чрезвычайно простых команд bash.
Объяснение:
Находит все файлы в текущем каталоге.
Сортирует по алфавиту
Показывать только последние 4 строки.
Зацикливается на каждой строке, выполняя команду копирования.
источник
Пока вы находите, что сортировка оболочки приемлема, вы можете просто сделать:
Это очень похоже на
bash
предлагаемое решение для конкретного массива, но должно переноситься на любую POSIX-совместимую оболочку. Некоторые заметки об обоих методах:Важно, чтобы вы приводили свои
cp
аргументы w /cp --
или вы получали одну из.
точек или/
заголовок каждого имени. Если вам не удастся сделать это, вы рискуете первым аргументом первой-
черты вcp
начале, который может быть интерпретирован как опция и может привести к сбою операции или к непредвиденным результатам.set -- ./*
илиarray=(./*)
.При использовании обоих методов важно, чтобы в вашем массиве arg было как минимум столько элементов, сколько вы пытаетесь удалить - я делаю это здесь с математическим расширением. Это
shift
происходит только в том случае, если в массиве arg есть по крайней мере 4 элемента, а затем удаляются только те первые аргументы, которые дают излишек в 4 ..set 1 2 3 4 5
случае это сместит1
прочь, но вset 1 2 3
случае это ничего не сместит.a=(1 2 3); echo "${a[@]: -4}"
печатает пустую строку.Если вы копируете из одного каталога в другой, вы можете использовать
pax
:... который применил бы
sed
подстановку -style ко всем именам файлов при их копировании.источник
Если есть только файлы и их имена не содержат пробелов,
$IFS
символов новой строки (и вы не изменили ) или символов глобуса (или некоторых непечатаемых символов с некоторыми реализациямиls
) и не начинаются с.
, то вы можете сделать это :источник
a_
ко ВСЕМ четырем файлам? Я пыталсяecho "a_$(ls | tail -n 4)"
, но это только prepends первый файл.ls
вывод синтаксического анализа считается дурным тоном, поскольку обычно он приводит к ужасным ошибкам в именах файлов, которые содержат не только пробелы, но также символы табуляции, новые строки или другие допустимые, но "сложные" символы.Это простое решение bash без какого-либо цикла кода. Скопируйте последние 4 файла из текущего каталога в место назначения:
источник
find
отдаете файлы в нужном порядке? Как вы гарантируете, что файлы, содержащие пробельные символы (включая символы новой строки) в их именах, обрабатываются правильно?