Скопіюйте групу файлів (Ім'я файлу *) в резервну копію (Ім'я файлу * .bak)


13

Фон

У Linux ви можете:

  • Перерахуйте групу файлів за допомогою ls Filename*
  • Видаліть групу файлів за допомогою rm Filename*
  • Перемістіть групу файлів за допомогою mv Filename* /New/Directory
  • Але ви не можете скопіювати групу файлів за допомогою:cp Filename* *.bak

Змініть cpкоманду Linux, щоб скопіювати групу файлів

У мене є група файлів, яку я хотів би скопіювати, не вводячи імена по черзі і не використовуючи cpкоманду:

$ ls gmail-meta3*
gmail-meta3                          gmail-meta3-REC-1558392194-26467821
gmail-meta3-LAB-1558392194-26467821  gmail-meta3-YAD-1558392194-26467821

Як я можу використовувати щось на зразок старої команди DOS copy gmail-meta3* *.bak?

Я не хочу вводити подібну команду чотири рази:

cp gmail-meta3-LAB-1558392194-26467821 gmail-meta3-LAB-1558392194-26467821.bak

Я шукаю сценарій / функцію / додаток, який приймає параметри для старої та нової групи імен файлів, а не щось із жорстко кодованими іменами. Наприклад, користувач може вводити:

copy gmail-meta3* *.bak

або вони можуть набрати:

copy gmail-meta3* save-*

1
Проблема виглядає як я використовую глобальний оператор двічі, який не використовує жодна з інших команд. Баш недостатньо розумний, щоб впоратися з цим.
qwr

2
@qwr, той факт, що bash розширює метахарактеристики і токенізує вхід до передачі його команді, яку слід виконати, є частиною дизайну оболонок UNIX. Щоб якось спробувати і запрограмувати виняток для команди cp, було б порушено всю узгодженість bash, яка взагалі не була б розумною. Як вправу, спробуйте з’ясувати, що тут відбувається, і чому саме так touch aa ab ba; mkdir bb; cp a* b*; ls *
Майк Ш

@MikeS Дякую за покажчики. Вчора хтось ще сказав, що ви можете використовувати підстановку *для вихідних імен файлів, але не для цільових імен. Отож, замінюючи підстановку (я думаю, що ##було запропоновано, але я схиляюся %), доведеться використовувати для цілі. Я думаю, це саме те, що ти підкріплюєш? Я зовсім не сподівався змінити cpкоманду. Просто створіть скрипт для обгортки, який називається copyемульованою (в межах причини) командою копіювання DOS.
WinEunuuchs2Unix

@ WinEunuuchs2Unix ця людина була вірна. Метахарактеристики оболонки не залежать від команд. Тож усі підстановочні знаки намагатимуться зіставити всі файли, які відповідають шаблону. Якщо ви намагаєтеся зробити загальну мету "відповідати всім і скопіювати їх у будь-яку програму, але додайте цей суфікс", тоді так, якщо невказаний метахарактер як ціль, ймовірно, не буде робити те, що ви хочете. Тому що всі метахарактори в командному рядку оболонки розширені. Якщо ви точно знаєте, що цільовий метахарактер ніколи не утворюватиме відповідність, ви можете використовувати його, оскільки оболонка не може його розширити.
Майк Ш

... але це було б потворно. Краще використовувати спеціальний символ. % або підкреслення добре, вони, як правило, не є метахарактеристиками (але будьте обережні, використовуючи% у файлі crontab; там це особливо).
Майк Ш

Відповіді:


14

Ось приклад одного нетипового використання sed, що застосовується для цього завдання:

sed -i.bak '' file-prefix*

Таким чином, насправді sedфайли не змінять, оскільки ми не надали жодних команд '', але завдяки опції -i[suffix]це створить резервну копію кожного файлу. Я знайшов такий підхід під час пошуку Чи є спосіб створити резервну копію файлу, не вводячи його ім'я двічі?


FYI: $ time sed -i.bak '' gmail-meta3*=real 0m0.069s
WinEunuuchs2Unix

Якщо файли вже існує , то: real 0m0.037s. Якщо файли видалені і запустити другий раз близько до cpшвидкості: real 0m0.051s.
WinEunuuchs2Unix

@xiota Цікавий момент. Виявляється sedшвидше, коли цільові файли вже існують, але cpповільніше, коли існують цільові файли. Насправді я стираю кеші та буфери, а не syncколи роблю великі тести на хронометраж, але цього разу я цього не робив. Оскільки це може втягнутись у зовсім іншу мильну оперу без тематики, я шкодую, що поділився результатами тесту :( Чи занадто пізно робити вигляд, що ця розмова ніколи не відбулася? Розмір метаданих повідомлення FYI gmail - 2,5 МБ, а 3 файли індексів - близько 800 КБ Також це не жорсткий диск, це SSD Samsung Pro 960 NVMe на 4-х каналах
WinEunuuchs2Unix

1
Мабуть, не має значення, який у вас є пристрій для зберігання даних, якщо це відбувається на машині Linux. Ядро надзвичайно добре захищає файли в пам'яті. Ось яке значення має "buff / cache" при використанні freeкоманди. Фактичні записи на пристрій відбуваються в момент, обраний алгоритмом, який враховує вік кешу і тиск пам'яті на машині. Якщо ви намагаєтеся виконати кілька тестів, то перші диски з файлу вийдуть з диска, але наступні читання, швидше за все, вийдуть прямо з пам'яті (див. sync; echo 3 > /proc/sys/vm/drop_caches).
Майк Ш

Вчора я зробив кілька тестів з великими файлами понад 2 Гб і - так, такий підхід відносно повільний, ніж використання cpкоманди, але не можу сказати, що є значна різниця в продуктивності .
pa4080

13

Ви можете використовувати find:

find . -max-depth 1 -name 'gmail-meta3*' -exec cp "{}" "{}.bak" \;

Це знайде в поточному каталозі .всі файли з іменем, що відповідає глобальному шаблону (пам’ятайте про єдині лапки навколо шаблону, щоб запобігти поглинанню оболонки). Для кожного знайденого файлу він буде виконуватись cpвід імені до name.bak. \; врешті-решт гарантує, що він буде робити кожен файл окремо, а не передавати їх усі одразу. Максимальна глибина, як 1, здійснює пошук лише в каталозі cuurent, а не повторюється вниз.


1
Чи буде це працювати, find . -max-depth 1 -name '"$1"'' -exec cp "{}" "{}$2" \;коли $ 1 є джерелом, а $ 2 - розширенням?
WinEunuuchs2Unix

$ 2 має бути нормально замінити, доки це розумно. $ 1 може бути складнішим, оскільки ми не можемо робити змінну підстановку в межах однієї лапки. Я не впевнений, що напевно, але можливо, можливо, використовувати $ 1 у подвійних лапках, оскільки шаблон зберігається в рядку.
cbojar

11

Ви можете використовувати forцикл за допомогоюbash . Зазвичай я б просто вводив його як однолінійний, оскільки це не завдання, яке я виконую часто:

for f in test* ; do cp -a "$f" "prefix-${f}.ext" ; done

Однак якщо вам це потрібно як сценарій:

cps() {
   [ $# -lt 2 ] && echo "Usage: cps REGEXP FILES..." && return 1

   PATTERN="$1" ; shift

   for file in "$@" ; do
      file_dirname=`dirname "$file"`
      file_name=`basename "$file"`
      file_newname=`echo "$file_name" | sed "$PATTERN"`

      if [[ -f "$file" ]] && [[ ! -e "${file_dirname}/${file_newname}" ]] ; then
         cp -a "$file" "${file_dirname}/${file_newname}"
      else
         echo "Error: $file -> ${file_dirname}/${file_newname}"
      fi
   done
}

Використання подібне до rename. Тестувати:

pushd /tmp
mkdir tmp2
touch tmp2/test{001..100}     # create test files
ls tmp2
cps 's@^@prefix-@ ; s@$@.bak@' tmp2/test*    # create backups
cps 's@$@.bak@' tmp2/test*    # more backups ... will display errors
ls tmp2
\rm -r tmp2                   # cleanup
popd

FYI: $ time for f in gmail-meta3* ; do cp -a "$f" "${f}.bak" ; done=real 0m0.046s
WinEunuuchs2Unix

Гм ні, я не хочу оптимізувати час. Це 0.046секунди, що означає 0 секунд для сприйняття людиною. Я просто намагався показати, як я тестував опубліковані відповіді і передав цікаві підказки глядачам, які переглядали sedкоманду вище. Або принаймні мене зацікавило порівняння sedз cp....
WinEunuuchs2Unix

Ваше рішення з cpшвидшим, ніж рішення, sedхоча. Тож це привід для святкування :)
WinEunuuchs2Unix

(1)  -aє нестандартним оператором тестування. Чому б не використовувати -e? (2) "Неможливо створити тимчасовий каталог." є дещо оманливим повідомленням про помилку. (3) Чому б не просто використовувати mktemp -d? (4) Ви повинні перевірити статуси виходу. Наприклад, слід сказати ! mkdir "$FOLDER" && echo "Unable to create temporary directory." && return 1 або  mkdir "$FOLDER" || { echo "Unable to create temporary directory."; return 1;}. Так само cpі  для rename(і, можливо, навіть pushd, якщо ви хочете бути обережними). … (Продовжував)
G-Man каже: «Відновити Моніку»

(Продовження)… (5) Arrggghhhh! Не кажіть $@; сказати "$@". (5б)  Там немає необхідності використовувати {і } коли ви посилатися на змінні, як ви робите ( "${FOLDER}",  "${PATTERN}" і  "${file}"); просто робити "$FOLDER",  "$PATTERN" і  "$file". (6) Це передбачає, що файли знаходяться в поточному каталозі.  cps 's/$/.bak/' d/fooскопіює d/fooдо foo.bak нинішнього року каталозі, а НЕ d/foo.bak.
G-Man каже: "Відновіть Моніку"

6

Найближчий до вас, ймовірно, потрапить до парадигми DOS - це mcpmmvпакета):

mcp 'gmail-meta3*' 'gmail-meta3#1.bak'

Якщо zshє, його внесений zmvмодуль, можливо, трохи ближче:

autoload -U zmv

zmv -C '(gmail-meta3*)' '$1.bak'

Я б уникнув lsнезалежно - варіант із власної відповіді безпечний для пробілів (включаючи нові рядки)

printf '%s\0' gmail-meta3* | while IFS= read -r -d '' f; do cp -a -- "$f" "$f.bak"; done

чи, можливо,

printf '%s\0' gmail-meta3* | xargs -0 -I{} cp -a -- {} {}.bak

Я розумію, що mmvце пакет, але в коментарях ви говорите, що команда є, mcpале тоді в команді, яку ви використовуєте, mmvце також команда в mmvпакеті. Мені подобається напрямок printfприкладів, і в відшліфованому сценарії я б гарантував, що $ 1 і $ 2 були передані. +1 за отримання прокатки м’яча :)
WinEunuuchs2Unix

@ WinEunuuchs2Unix вибачається - mcp / mmv був мозковим подальшим. Насправді mcpце лише синонімmmv -c
steeldriver

Pfft не хвилюється. Якби у мене був долар за кожну друкарську помилку, я був би мільйонером :) Я хотів би пояснення щодо printfкоманди, яку я ніколи не використовував. Ви хочете сказати, printf '%s\0' "$1"*що спрацювало б, якби gmail-meta3було передано як параметр 1?
WinEunuuchs2Unix

@ WinEunuuchs2Unix Я, мабуть, дозволю виклику контексту зробити глобус, cps gmail-meta3*а потім написати printf '%s\0"$ @" | в той час як ... `у функції. Або просто використовувати for f; do cp -- "$f" "$f.bak"; done(як відповідь xiota , але як функцію)
steeldriver

1
Зауважте, що zmvви можете використовувати режим «заміни підстановки», який мені здається трохи простіше:zmv -W -C 'gmail-meta3*' '*.bak'
0x5453

5

рішення тільки для rsync

Якщо ви просто хочете створити резервну копію своїх файлів, ви можете скопіювати їх у новий каталог

rsync /path/to/dir/Filename* /path/to/backupdirectory

Це скопіює Filenameфайли з /path/to/dir/у /path/to/backupdirectory.


rsync + ім'я файлу

Якщо ви хочете, щоб у ваших файлах резервного копіювання був суфікс, речі стають нескінченними із rsync...

rsync -Iu /path/to/dir/Filename* /path/to/dir/Filename* -b --backup-dir=/path/to/backupdirectory --suffix=.bak

Це дозволить перезаписати наявні файли ... з існуючими файлами ( -I), але лише у тому випадку, якщо вони ( -u) новіші (які вони не є) та створюють резервну копію із суфіксом.

Ви також можете зробити це в одному каталозі. Але краще виключити існуючі резервні копії.

rsync -Iu /path/to/dir/Filename* /path/to/dir/Filename* -b --backup-dir=/path/to/backupdirectory --suffix=.bak --exclude '*.bak'


Мені подобається, що rsycncя схвалив, але більш простим методом було б cp Filename* /path/to/backup/dirтому, що файлам не знадобиться *.bakуніфікатор, якби вони були в окремому каталозі.
WinEunuuchs2Unix

4

Це потрібно зробити так, як вимагається:

cps(){ p="${@: -1}"; for f in "${@:1:$#-1}"; do cp -ai "$f" "${p//\?/$f}"; done  }

Використання:

cps FILES... pattern
Example 1: cps gmail-meta3* ?.bak
Example 2: cps * save-?
Example 3: cps * bla-?-blubb

Я вибрав, ?тому що його #треба цитувати, коли це перший символ візерунка, інакше його визнають як початок коментаря.

Тест:

$ touch 'test};{bla#?"blubb'
$ cps test* bla-?-blubb
$ ls
test};{bla#?"blubb  bla-test};{bla#?"blubb-blubb


Деякі попередні версії сценарію для додавання суфікса:

Схожий на відповідь @ WinEunuuchs2Unix, але я вважаю більш гнучким і не розбираєls :

cps(){ S="$1"; shift; printf '%s\0' "$@" | xargs -0 -I{} cp -abfS "$S" {} {}; }

Покладіть це у своє .bashrc.

Використання:

cps SUFFIX FILES...
Example: cps .bak gmail-meta3*

Альтернативно, із суфіксом як останнім аргументом ( через та через ):

cps(){ S="${@: -1}"; printf '%s\0' "${@:1:$#-1}" | xargs -0 -I{} cp -abfS "$S" {} {}; }

Використання:

cps FILES... SUFFIX
Example: cps gmail-meta3* .bak


Приємне кодування, але це важко після десятиліть використання Source, а потім Target, щоб переключити команду copy на Target, а потім на Source
WinEunuuchs2Unix

Додана функція із суфіксом на звороті.
pLumo

Завдяки цьому більш інтуїтивно зрозуміло. Називаючи його суфіксом - це точний спосіб, коли його відповідь зашифрована, але це дійсно ціль або пункт призначення. Інші користувачі можуть хотіти використовувати: copy gmail-meta3* old-meta3*. У своїй відповіді я не міг зрозуміти, як потрапити *до назви призначення, як запитав моє запитання ...
WinEunuuchs2Unix

Проблема в тому, що *інтерпретується оболонкою, тому функція не буде знати про неї. Вам знадобиться якийсь інший символ або процитувати його, а потім замінити його оригінальним іменем файлу в межах функції.
pLumo

Я здогадуюсь, чи #може бути використаний замісний підстановочний знак *? Таким чином, ви можете набрати copy filenames# save-#. Я думаю, ви хочете, щоб символ підстановки був однаковим для джерела та цілі.
WinEunuuchs2Unix

4

Я записав цей однокласник у свій ~/.bashrc. findЯ думаю, що набагато кращі відповіді можна використовувати . Ще кращі відповіді можна було б написати на C. Сподіваємось, це питання і відповіді отримують м'яч, щоб отримати кращі відповіді:

cps () {
    # cps "Copy Splat", copy group of files to backup, ie "cps Filename .bak"
    # Copies Filename1 to Filename1.bak, Filename2 to Filename2.bak, etc.
    # If Filename1.bak exists, don't copy it to Filename1.bak.bak
    for f in "$1"*; do [[ ! "$f" == *"$2" ]] && cp -a "$f" "$f$2"; done

    # OLD version comments suggested to remove 
    # ls "$1"* | while read varname; do cp -a "$varname" "$varname$2"; done
}
  • for f in "$1"*; do: $1є gmail-meta3параметром і fє списком файлів, які відповідають. У поєднанні цього засобу для gmail-meta3, gmail-meta3-LAB-9999 тощо виконайте наступне
  • [[ ! "$f" == *"$2" ]] &&: $fте саме, що fвище. $2- це .bakпараметр, що передається. У поєднанні це означає, що ім'я файлу не закінчується .bak(тому що ми не хочемо копіювати .bakта створювати .bak.bak), тоді виконайте наступне
  • cp -a "$f" "$f$2"; скопіюйте gmail-meta3 в gmail-meta3.bak тощо.
  • done: циклічно поверніть та захопіть наступне ім'я файлу у gmail-meta3списку *.

cps gmail-meta3 .bak Вибірка зразка

Використовуючи питання як приклад, ось як це виглядає в дії:

───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/gmail$ ll gmail-meta3*
-rw-rw-r-- 1 rick rick 26467821 May 20 16:43 gmail-meta3
-rw-rw-r-- 1 rick rick 26467821 May 20 16:43 gmail-meta3.bak
-rw-rw-r-- 1 rick rick      643 May 20 16:43 gmail-meta3-LAB-1558392194-26467821
-rw-rw-r-- 1 rick rick      643 May 20 16:43 gmail-meta3-LAB-1558392194-26467821.bak
-rw-rw-r-- 1 rick rick    49607 May 20 16:44 gmail-meta3-REC-1558392194-26467821
-rw-rw-r-- 1 rick rick    49607 May 20 16:44 gmail-meta3-REC-1558392194-26467821.bak
-rw-rw-r-- 1 rick rick   728954 Jun 27 17:04 gmail-meta3-YAD-1558392194-26467821
-rw-rw-r-- 1 rick rick   728954 Jun 27 05:46 gmail-meta3-YAD-1558392194-26467821.bak
───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/gmail$ cps gmail-meta3 .bak
───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/gmail$ ll gmail-meta3*
-rw-rw-r-- 1 rick rick 26467821 May 20 16:43 gmail-meta3
-rw-rw-r-- 1 rick rick 26467821 May 20 16:43 gmail-meta3.bak
-rw-rw-r-- 1 rick rick      643 May 20 16:43 gmail-meta3-LAB-1558392194-26467821
-rw-rw-r-- 1 rick rick      643 May 20 16:43 gmail-meta3-LAB-1558392194-26467821.bak
-rw-rw-r-- 1 rick rick    49607 May 20 16:44 gmail-meta3-REC-1558392194-26467821
-rw-rw-r-- 1 rick rick    49607 May 20 16:44 gmail-meta3-REC-1558392194-26467821.bak
-rw-rw-r-- 1 rick rick   728954 Jun 27 17:04 gmail-meta3-YAD-1558392194-26467821
-rw-rw-r-- 1 rick rick   728954 Jun 27 17:04 gmail-meta3-YAD-1558392194-26467821.bak
───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/gmail$ 

Примітка. Для цього використовується -aпрапор із cpкомандою для збереження часових позначок та кращого розуміння резервних копій файлів.

Зверніть увагу, як копії файлів мають таку саму дату та час, що й оригінали. Якщо -aпараметр було опущено, вони отримали б поточну дату та час і не виглядали б як справжня резервна копія, за винятком того, що розмір файлу був би однаковим.


6
не рекомендують людей завжди проти розборуls
qwr

3
Оскільки ви згадуєте, findя припускаю, що ви знаєте про небезпеку розбору ls? Але у вашому випадку це і не потрібно: просто робіть for file in "$1"*; do copy -a "$file" "$file$2"; done- це абсолютно безпечно і набагато простіше, ніж будь-який вид непрямості через lsабо findі whileцикл.
Конрад Рудольф

@KonradRudolph Дякую за вашу пропозицію. Я реалізував та протестував вашу пропозицію з парою незначних змін.
WinEunuuchs2Unix

2

Ще один спосіб досягнення вашої вимоги - скопіювати файли у тимчасовий каталог та використовувати renameкоманду для перейменування їх.

$ mkdir backup
$ cp filename* /tmp/rename-backup/
$ rename 's/(filename.*)/$1.bak/' /tmp/rename-backup/*
$ mv /tmp/rename-backup/* ./

Якщо він вам потрібен як сценарій, ви можете використовувати його так

cps () {
    mkdir -p /tmp/rename-backup/
    cp "$1"* /tmp/rename-backup/
    rename "s/($1.*)/\$1.$2/" /tmp/rename-backup/*
    mv "/tmp/rename-backup/$1"*".$2" .
}

І ви можете використовувати його так:

cps file bak

Це приклад

$ ls -l
total 0
-rw-r--r--  1 danny  danny  0 Jun 26 16:23 file a
-rw-r--r--  1 danny  danny  0 Jun 26 16:23 file ab
-rw-r--r--  1 danny  danny  0 Jun 26 16:23 file ac
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename1
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename2
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename3
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename4
$ cps file bak
$ ls -l
total 0
-rw-r--r--  1 danny  danny  0 Jun 26 16:23 file a
-rw-r--r--  1 danny  danny  0 Jun 26 16:41 file a.bak
-rw-r--r--  1 danny  danny  0 Jun 26 16:23 file ab
-rw-r--r--  1 danny  danny  0 Jun 26 16:41 file ab.bak
-rw-r--r--  1 danny  danny  0 Jun 26 16:23 file ac
-rw-r--r--  1 danny  danny  0 Jun 26 16:41 file ac.bak
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename1
-rw-r--r--  1 danny  danny  0 Jun 26 16:41 filename1.bak
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename2
-rw-r--r--  1 danny  danny  0 Jun 26 16:41 filename2.bak
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename3
-rw-r--r--  1 danny  wheel  0 Jun 26 16:41 filename3.bak
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename4
-rw-r--r--  1 danny  danny  0 Jun 26 16:41 filename4.bak
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.