Каманда cp: правільнае капіраванне тэчак з файламі ў *nix
У гэтым артыкуле будуць раскрыты некаторыя невідавочныя рэчы, звязаныя з выкарыстаннем падстаноўныя знакі пры капіяванні, неадназначныя паводзіны каманды cp пры капіяванні, а таксама спосабы якія дазваляюць карэктна капіяваць велізарную колькасць файлаў без пропускаў і вылетаў.
Дапушчальны нам трэба скапіяваць усё з тэчкі /source у тэчку /target.
Першае, што прыходзіць на розум гэта:
cp /source/* /target
Адразу выправім гэтую каманду на:
cp -a /source/* /target
ключ -a дадасць капіраванне ўсіх аттрыбутаў, правоў і дадасць рэкурсію. Калі не патрабуецца дакладнае прайграванне правоў дастаткова ключа -r.
Пасля капіявання мы выявім, што скапіяваліся не ўсе файлы - былі праігнараваныя файлы, якія пачынаюцца з кропкі тыпу:
.profile
.local
.mc
і таму падобныя.
Чаму ж так адбылося?
Таму што wildcards апрацоўвае shell (bash у тыпавым выпадку). Па змаўчанні bash праігнаруе ўсе файлы пачынаюцца з кропак, бо тлумачыць іх як утоеныя. Каб пазбегнуць такіх паводзін нам давядзецца змяніць паводзіны bash з дапамогай каманды:
shopt -s dotglob
Каб гэтая змена паводзін захавалася пасля перазагрузкі, можна зрабіць файл wildcard.sh з гэтай камандай у тэчцы /etc/profile.d (магчыма ў вашым дыстрыбутыве іншая тэчка).
А калі ў дырэкторыі-крыніцы няма файлаў, то shell не зможа нічога падставіць замест зорачкі, і таксама капіраванне завершыцца з памылкай. Супраць падобнай сітуацыі ёсць опцыі failglob и nullglob. Нам спатрэбіцца выставіць failglob, якая не дасць камандзе выканацца. nullglob не падыдзе, бо яна радок з wildcards не якія знайшлі супадзення пераўтворыць у пусты радок (нулявой даўжыні), што для cp выкліча памылку.
Аднак, калі ў тэчцы тысячы файлаў і больш, то ад падыходу з выкарыстаннем wildcards варта адмовіцца зусім. Справа ў тым што bash разгортвае wildcards у вельмі доўгі камандны радок накшталт:
cp -a /souce/a /source/b /source/c …… /target
На даўжыню каманднага радка ёсць абмежаванне, якое мы можам даведацца выкарыстоўваючы каманду:
getconf ARG_MAX
Атрымаем максімальную даўжыню каманднага радка ў байтах:
2097152
Або:
xargs --show-limits
Атрымаем нешта тыпу:
….
Maximum length of command we could actually use: 2089314
….
Дык вось, давайце будзем абыходзіцца зусім без wildcards.
Давайце проста напішам
cp -a /source /target
І тут мы сутыкнемся з неадназначнасцю паводзін cp. Калі тэчкі /target не існуе, мы атрымаем тое, што нам трэба.
Аднак, калі тэчка target існуе, то файлы будуць скапіяваныя ў тэчку /target/source.
Не заўсёды мы можам выдаліць загадзя тэчку /target, бо ў ёй могуць быць патрэбныя нам файлы і наша мэта, дапусцім, дапоўніць файлы ў /target файламі з /source.
Калі б тэчкі крыніцы і прымача зваліся аднолькава, напрыклад, мы капіявалі б з /source у /home/source, то можна было б выкарыстаць каманду:
cp -a /source /home
І пасля капіявання файлы ў /home/source апынуліся б дапоўненымі файламі з /source.
Такая вось лагічная задача: мы можам дапоўніць файлы ў дырэкторыі-прымачы, калі тэчкі завуцца аднолькава, але калі яны адрозніваюцца, то тэчка-зыходнік будзе змешчаная ўнутр прымача. Як скапіяваць файлы з /source у /target з дапамогай cp без wildcards?
Каб абыйсці гэтае шкоднае абмежаванне мы выкарыстоўваем невідавочнае рашэнне:
cp -a /source/. /target
Тыя хто добра знаёмы з DOS і Linux ужо ўсё зразумелі: усярэдзіне кожнай тэчкі ёсць 2 нябачныя тэчкі "." і «..», якія з'яўляюцца псевдопапками-спасылкамі на бягучую і вышэйстаячыя дырэкторыі.
Пры капіяванні cp правярае існаванне і спрабуе стварыць /target/.
Такая дырэкторыя існуе і гэта ёсьць /target
Файлы з /source скапіяваныя ў /target карэктна.
Такім чынам, вешаем у тоўстую рамачку ў сваёй памяці або на сцяне:
cp -a /source/. /target
Паводзіны гэтай каманды адназначна. Усё адпрацуе без памылак па-за залежнасцю ад таго мільён у вас файлаў ці іх няма зусім.
Высновы
Калі трэба скапіяваць ўсё файлы з адной тэчкі ў іншую, не выкарыстоўваны wildcards, замест іх лепш выкарыстоўваць cp у спалучэнні з кропкай у канцы тэчкі-крыніцы. Гэта скапіюе ўсе файлы, уключаючы ўтоеныя і не заваліцца пры мільёнах файлаў або поўнай адсутнасці файлаў.
пасляслоўе
vmspike прапанаваў аналагічны па выніку варыянт каманды: