Трубний вихід команди, якщо вона успішна


14
INPUT_FILE=`ls -rt $MY_DIR/FILE.*.xml | head -1 | xargs basename`

Я хотів виконати другу команду ( head -1), тільки якщо перша команда успішна. Як я вдосконалюю цю команду?


Що ви маєте на увазі під успішним? lsне підведе.
Маттео

2
Але глобус може вийти з ладу, якщо немає відповідних файлів.
tripleee

Відповіді:


8

Спробуйте це:

INPUT_FILE=`ls -rt "$MY_DIR"/FILE.*.xml | head -1 | xargs -r basename`

Переходячи xargsдо -rпрапор змусить його працювати тільки , basenameякщо зчитує щонайменше один елемент зі стандартного вводу ( head -1).

head -1 запуститься, але ви не побачите і не захопите жодного результату з нього.

Крім того, якщо ви не хочете, щоб користувач бачив якісь помилки ls, ви можете перенаправити lsпотоковий stderr на /dev/null.

INPUT_FILE=`ls -rt "$MY_DIR"/FILE.*.xml 2> /dev/null | head -1 | xargs -r basename`

Також зауважте, що я додав лапки навколо $MY_DIR. Таким чином, команда не вийде з ладу, якщо $MY_DIRмістить пробіли.

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

input_file=$(ls -rt "$my_dir"/FILE.*.xml 2> /dev/null | head -1 | xargs -r basename)

Чи не це b0rk, якщо немає файлів?
Мел Бойс

Власне: ls -rt GLOB 2>/dev/null | head -n1 | xargs -r basenameпрацює.
Мел Бойс

@MelBoyce: Я додав це до своєї відповіді. Дякую.

ls - це інструмент для інтерактивного перегляду інформації про файли. Його вихід відформатований для людей і призведе до помилок у сценаріях. Використовуйте глобуси або знаходьте замість цього. Зрозумійте, чому: mywiki.wooledge.org/ParsingLs
cinelli

@cinelli: Це правда. Я тільки відповідав на питання.

9

У трубопроводі всі команди запускаються та виконуються одночасно, а не одна за одною. Тож вам потрібно десь зберігати вихід.

if ls_output=$(ls -rtd -- "$MY_DIR"/FILE.*.xml); then
  first_file=$(printf '%s\n' "$ls_output" | head -n 1)
  first_file_name=$(basename -- "$first_file")
fi

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

Щоб отримати базове ім'я найстарішого файлу zshбез будь-якого з цих обмежень для символів (також уникає проблеми обмеженого розміру аргументів для команди):

 first_file_name=($MY_DIR/FILE.*.xml(Om[1]:t))

Якщо збігу немає, ця команда не вдасться і скасувати сценарій. Ви також можете зробити:

 first_file_name=($MY_DIR/FILE.*.xml(NOm[1]:t))

У такому випадку first_file_nameмасив буде містити 1 елемент, якщо є збіг, або 0, якщо ні. Потім ви можете зробити:

 if (($#first_file_name)); then
    printf 'Match: %s\n' $first_file_name
 else
    echo >&2 No match
 fi

4

Знайдіть останній модифікований файл у каталозі:

latest() {
  local file path=${1:-.} ext=${2-} latest
  for file in "${path%/}"/*"$ext"; do 
    [[ $file -nt $latest ]] && latest=$file
  done
  [[ $latest ]] && printf '%s\n' "$latest"
}

Використання: latest [directory/path/ [.extension]]

Замість того, щоб викликати basename, використовуйте розширення параметрів.

in_file=$(latest in/my/dir .xml)
base_fn=${in_file##*/} base_fn=${base_fn%.*}

У каталозі з цим вмістом:

foo.xml bar.xml baz.xml newest.xml

Вміст змінної base_fn був би: newest

Щоб правильно використовувати це для задоволення мети вашого запиту:

check_dir=/path/to/check
check_ext=.xml
if in_file=$(latest "$check_dir" "$check_ext"); then
  base_fn=${in_file##*/} base_fn=${base_fn%.*}
else
  printf '%s\n' "No file found in $check_dir" >&2
fi

EDIT: переглянувши питання, я зрозумів, що lsкоманда, про яку йдеться, шукає найдавніший файл у каталозі. цю ж функцію можна було б перейменовувати oldestі [[ $file -ot $oldest ]] && oldest=$fileзамість цього досягти того ж ефекту. вибачення за будь-яку плутанину.

Важливо зазначити, що ви абсолютно ні за яких обставин у людстві не повинні розбирати вихід лс. ніколи.


Зауважте, що всупереч lsбазованим підходам, якщо є посилання, час модифікації цілі символьних посилань буде враховано (додайте -Lопцію, lsщоб отримати ту саму поведінку).
Стефан Шазелас

0

Я думаю, що найкращий варіант тут:

ls -rf $MY_DIR/FILE.*.xml
if [ $? -eq 0 ]; then
      INPUT_FILE=`ls -rt $MY_DIR/FILE.*.xml|head -1|xargs basename `
else
      echo "Error!"
fi

якщо немає файлу, тоді код повернення ls дорівнює 2, але якщо він знайде якийсь файл, буде 0.


2
Замість порівняння $?Against 0, ви можете зробити це: if ls -rf $MY_DIR/FILE.*.xml ; then.

Крім того, ви можете переспрямувати висновок першої команди на /dev/null. ls -rf $MY_DIR/FILE.*.xml &> /dev/null. Таким чином, користувач не побачить зайвий вихід.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.