Чи є спосіб, щоб змусити "мв" вийти з ладу?


61

Така команда mv foo* ~/bar/видає це повідомлення в stderr, якщо файли не відповідають foo*.

mv: cannot stat `foo*': No such file or directory

Однак у сценарії, над яким я працюю, це було б добре, і я хочу пропустити це повідомлення з наших журналів.

Чи є якийсь приємний спосіб сказати, mvщоб бути тихим, навіть якщо нічого не зворушили?


13
mv foo* ~/bar/ 2> /dev/null?
Томас Найман

1
Ну так. Я думаю, я мав на увазі щось "приємніше", як-от якийсь перемикач mv. :) Але це, звичайно, і зробить.
Jonik

3
Я бачив, що деякі mvреалізації підтримують -qможливість їх заспокоїти, але це не є частиною специфікації POSIX для mv. mvУ GNU Coreutils, наприклад, зовсім НЕ мають такий варіант.
Thomas Nyman

Ну, я не думаю, що проблема полягає в тому, як тримати "mv" тихо, але як це зробити більш правильно. Перевірка, чи є файл / dir foo *, і користувач має дозвіл на запис, нарешті виконати "mv", можливо?
Shâu Shắc

Відповіді:


46

Ви шукаєте це?

$ mv  file dir/
mv: cannot stat file’: No such file or directory
$ mv  file dir/ 2>/dev/null
# <---- Silent ----->

20
Зауважте, повернення значення все-таки! = 0, тому будь-який set -e(який слід використовувати в кожному скрипті оболонки) не вдасться. ви можете додати а, || trueщоб вимкнути чек для однієї команди.
рето

10
@reto set -eне слід використовувати в кожному скрипті оболонки, в багатьох випадках це робить управління помилками ще складнішим, ніж без нього.
Кріс Даун

1
@ChrisDown Я бачу вашу думку. Зазвичай це вибір між двома злами. Але якщо ви сумніваєтесь, я віддаю перевагу невдалому успішному пробігу, ніж вдалому провалу. (що не помічається, поки воно не стане видимим для клієнта / користувача). Сценарії оболонки - це велике шахтне поле для початківців, так легко помилитися з помилкою, і ваш сценарій перебуває без проблем!
рето

14

Насправді, я не вважаю, що вимкнення звуку mvє гарним підходом (пам’ятайте, що він може повідомити про вас і про інші речі, які можуть зацікавити ... напр., Відсутні ~/bar). Ви хочете відключити його лише в тому випадку, якщо ваше глобальне вираження не дає результатів. Насправді, швидше, не виконувати його взагалі.

[ -n "$(shopt -s nullglob; echo foo*)" ] && mv foo* ~/bar/

Не виглядає дуже привабливо, і працює лише в bash.

АБО

[ 'foo*' = "$(echo foo*)" ] || mv foo* ~/bar/

тільки крім вас в bashс nullglobнабором. Ви платите ціну в 3 рази повторення глобального шаблону.


Незначна проблема з другою формою: вона не може перемістити файл з ім'ям, foo*коли він єдиний у поточному каталозі, який відповідає глобулю foo*. Це можна вирішити за допомогою глобального вираження, який не відповідає собі буквально, жорстким. Напр [ 'fo[o]*' = "$(echo fo[o]*)" ] || mv fo[o]* ~/bar/.
фра-сан

13

find . -maxdepth 1 -name 'foo*' -type f -print0 | xargs -0r mv -t ~/bar/

- GNU mvмає хороший варіант "призначення першого" ( -t) і xargsможе пропустити виконання своєї команди, якщо немає вводу взагалі ( -r). Використання -print0та, -0відповідно, гарантує, що у файлах файлів не міститься пробілів та інших "смішних" речей.


2
Слід зазначити , що -maxdepth, -print0, -0і -rтакож GNU розширення (хоча деякі з них знаходяться в інших реалізаціях в даний час).
Стефан Шазелас

1
Poige, багато наших користувачів не використовують Linux. Тут є звичайною практикою і дуже корисно вказати, які частини відповіді не є переносними. Сайт називається " Unix & Linux", і багато людей використовують певну форму BSD або Unix, AIX або macOS або вбудовані системи. Не сприймайте це особисто.
тердон

Я нічого особисто не беру. Я хоч маю думку, що ви видалили, як я бачу зараз. Тому я повторюю: я вважаю, що ці коментарі "зауважте, це GNU" є досить безглуздими. Їх взагалі не варто додавати, насамперед, оскільки моя відповідь чітко вказує, що вона заснована на GNU-версії mv.
poige

5

Важливо усвідомити, що насправді оболонка розширює foo*їх до списку відповідних імен файлів, тому мало що mvможе зробити сама.

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

Тож тут, коли foo*не відповідає жодному файлу, замість того, щоб скасувати команду (як, наприклад, оболонки перед Борном та декілька сучасних оболонок), оболонка передає дослівний foo*файл mv, тому в основному просить mvперемістити названий файл foo*.

Цей файл не існує. Якби це було, воно фактично відповідало б шаблону, тому mvповідомляє про помилку. Якби шаблон був foo[xy]замість цього, ви mvмогли б випадково перемістити файл, названий foo[xy]замість файлів fooxта fooyфайлів.

Тепер, навіть у тих оболонках, які не мають цієї проблеми (до Борна, csh, tcsh, риби, zsh, bash -O failglob), ви все одно отримаєте помилку mv foo* ~/bar, але цього разу за оболонкою.

Якщо ви хочете вважати це не помилкою, якщо файлу немає, foo*і в цьому випадку нічого не переміщувати, спершу слід створити список файлів (таким чином, що не викликає помилок, наприклад, використовуючи nullglobпараметр деякі оболонки), і тоді лише дзвінок mv- це список не порожній.

Це було б краще, ніж приховувати всі помилки mv(як додавання 2> /dev/null), як ніби mvне вдається з будь-якої іншої причини, ви, мабуть, все ще хочете знати, чому.

в зш

files=(foo*(N)) # where the N glob qualifier activates nullglob for that glob
(($#files == 0)) || mv -- $files ~/bar/

Або використовуйте анонімну функцію, щоб уникнути використання тимчасової змінної:

() { (($# == 0)) || mv -- "$@" ~/bar/; } foo*(N)

zshє однією з тих оболонок, у яких немає помилки Bourne і повідомляють про помилку, не виконуючи команду, коли глобус не відповідає (а nullglobпараметр не ввімкнено), тож тут ви можете приховати zshпомилку та відновити stderr for, mvтому ви все одно побачите mvпомилки, якщо такі є, але не помилку щодо невідповідних глобусів:

(mv 2>&3 foo* ~/bar/) 3>&2 2>&-

Або ви можете використовувати, zargsщо також уникне проблем, якщо foo*глобус розшириться на занадто man файли.

autoload zargs # best in ~/.zshrc
zargs -r -- foo* -- mv -t ~/bar # here assuming GNU mv for its -t option

У ksh93:

files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/

В bash:

bashне має синтаксису, щоб увімкнути nullglobлише один глобус, а failglobпараметр скасовується, nullglobтому вам знадобляться такі речі, як:

saved=$(shopt -p nullglob failglob) || true
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
eval "$saved"

або встановіть параметри в підрозділі, щоб зберегти їх потрібно зберегти раніше і відновити після цього.

(
  shopt -s nullglob
  shopt -u failglob
  files=(foo*)
  ((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
)

В yash

(
  set -o nullglob
  files=(foo*)
  [ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)

В fish

У рибній оболонці поведінка nullglob є типовою для setкоманди, тому це просто:

set files foo*
count $files > /dev/null; and mv -- $files ~/bar/

POSIXly

У nullglobPOSIX shнемає жодної опції та жодного масиву, крім параметрів позиції. Існує хитрість, яку ви можете використовувати, щоб визначити, чи відповідає глобус чи ні:

set -- foo[*] foo*
if [ "$1$2" != 'foo[*]foo*' ]; then
  shift
  mv -- "$@" ~/bar/
fi

Використовуючи і a, foo[*]і foo*glob, ми можемо розмежовувати випадок, коли немає відповідного файлу, і той, де є один файл, який, можливо, викликається foo*(що set -- foo*не може зробити).

Більше читання:


3

Це, мабуть, не найкраще, але ви можете скористатися findкомандою, щоб перевірити, чи папка порожня чи ні:

find "foo*" -type f -exec mv {} ~/bar/ \;

1
Це дасть буквальний foo*шлях пошуку find.
Kusalananda

3

Я припускаю, що ви використовуєте bash, оскільки ця помилка залежить від поведінки bash для розширення нерівних глобусів до себе. (Для порівняння, zsh викликає помилку при спробі розгорнути неперевершений глобус.)

Отже, що з наступного рішення?

ls -d foo* >/dev/null 2>&1 && mv foo* ~/bar/

Це мовчки проігнорує функцію mvif ls -d foo*не вдається, а при реєстрації помилок у випадку ls foo*успіху, але mvне вдасться. (Будьте обережні, ls foo*можуть провалитися з інших причин, ніж foo*не існує, наприклад, недостатньо прав, проблема з ФС тощо. Тому такі умови мовчки ігноруються цим рішенням.)


Мене найбільше цікавить баш, так (і я поставив це питання як bash). +1 за врахування того факту, який mvможе вийти з ладу з інших причин, ніж їх foo*немає.
Jonik

1
(На практиці я, мабуть, не буду користуватися цим, оскільки це свого роду багатослівне, і точка lsкоманди не буде дуже зрозумілою для майбутніх читачів, принаймні, без коментарів.)
Jonik,

ls -d foo*може повернутися з ненульовим статусом виходу також з інших причин, як-от після ln -s /nowhere foobar(принаймні, з деякими lsреалізаціями).
Стефан Шазелас

3

Можна зробити, наприклад

mv 1>/dev/null 2>&1 foo* ~/bar/ або mv foo* ~/bar/ 1&>2

Докладніші відомості див: http://mywiki.wooledge.org/BashFAQ/055


mv foo* ~/bar/ 1&>2не замовчує команду, вона надсилає те, що було б у stdout, щоб також було на stderr.
Кріс Даун

а якщо ви використовуєте mingw під Windows (як я), замініть /dev/nullнаNUL
Rian Sanderson

Як працює ця команда? Що роблять амперсанди? Що означають 1і 2означають?
Аарон Франке

@AaronFranke 1 за замовчуванням є stdout, а 2 - stderr pipe, коли створюється процес Linux. Дізнайтеся більше про pipe в командах Linux. Ви можете почати тут - digitalocean.com/community/tutorials/…
Mithun B

2

Ви можете обдурити (портативно) за допомогою perl:

perl -e 'system "mv foo* ~/bar/" if glob "foo*"'

Будь ласка, прокоментуйте, перш ніж відмовитись.
Джозеф Р.

2

Якщо ви збираєтеся використовувати Perl, ви можете також пройти весь шлях:

#!/usr/bin/perl
use strict;
use warnings;
use File::Copy;

my $target = "$ENV{HOME}/bar/";

foreach my $file (<foo*>) {
    move $file, $target  or warn "Error moving $file to $target: $!\n";
}

або як однолінійний:

perl -MFile::Copy -E 'move $_, "$ENV{HOME}/bar/" or warn "$_: $!\n" for <foo*>'

(Детальну інформацію про moveкоманду див. У документації до Файл :: Копіювати .)


-1

Натомість

mv foo* ~/bar/

Ви можете зробити

cp foo* ~/bar/
rm foo* 

Простий, читабельний :)


6
Копіювання та видалення займає більше часу, ніж простий хід. Він також не працюватиме, якщо файл більше, ніж наявний вільний простір на диску.
Антон

11
ОП хоче рішення, яке мовчить. Ні, cpні rmмовчати, якщо foo*його не існує.
ron rothman

-1
mv foo* ~/bar/ 2>/dev/null

Чи вдала команда Above, чи ні, ми можемо дізнатися за статусом виходу попередньої команди

command: echo $?

якщо вихід echo $? є відмінним від 0 означає, що команда є невдалою, якщо вихід 0 - це команда успішна

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.