Тест, если существуют файлы, соответствующие шаблону для выполнения сценария

Когда Вы хотите изменить файл, у Вас есть две опции, каждый с его преимуществами и недостатками.

  • Можно перезаписать файл на месте. Это не использует дополнительного пространства и сохраняет жесткие ссылки, полномочия и любой другой атрибут вне содержания существующего файла. Главный недостаток выполнения этого состоит в том, что, если что-нибудь происходит, в то время как файл пишется (сбои приложения, или питание выходит), Вы заканчиваете с частично записанным файлом.
  • Можно записать новую версию файла в новый файл с другим именем, затем переместить его в место. Это использует больше пространства и разрывает жесткие связи, и если у Вас есть полномочия записи на файле, но не на каталоге оно содержит, Вы не можете сделать этого вообще. На обороте старая версия файла атомарно заменяется новой версией, таким образом, в каждом моменте времени имя файла указывает на допустимую, полную версию файла.

Усовершенствованные редакторы, такие как Vim или Emacs могут выбрать между этими двумя методами. Когда они используют первый метод, они обычно делают файл резервной копии сначала, который может быть восстановлен, если новое содержание файла не может быть записано правильно.

Классический vi перезаписывает файл на месте. Таким образом, inode неизменен.

В Vim, поскольку cjm уже указал, выбором управляют backup, backupcopy и writebackup опции. По умолчанию Vim переименовывает старый файл, затем пишет новый файл с настоящим именем, если это думает, что может воссоздать атрибуты исходного файла. Если Вы хотите снова использовать существующий inode (и так рискнуть терять данные или тратить впустую больше времени, делая резервную копию), добавить set backupcopy yes к Вашему .vimrc.

29
21.10.2019, 20:48
10 ответов
[ -f /*.txt ]

возвратил бы true, только если существует один (и только один) нескрытый файл в / чье имя заканчивается в .txt и если тот файл является регулярным файлом или символьной ссылкой на регулярный файл.

Поэтому подстановочные знаки расширены оболочкой до того, чтобы быть переданным команде (здесь [).

Таким образом, если существует a /a.txt и /b.txt, [ будет передан 5 аргументов: [, -f, /a.txt, /b.txt и ]. [ затем жаловался бы это -f дан слишком много аргументов.

Если Вы хотите проверить что *.txt шаблон расширяется по крайней мере до одного нескрытого файла (регулярный или не):

shopt -s nullglob
set -- *.txt
if [ "$#" -gt 0 ]; then
  ./script "$@" # call script with that list of files.
fi
# Or with bash arrays so you can keep the arguments:
files=( *.txt )
# apply C-style boolean on member count
(( ${#files[@]} )) && ./script "${files[@]}"

shopt -s nullglob bash конкретный, но оболочки как ksh93, zsh, yash, tcsh имейте эквивалентные операторы.

Обратите внимание, что это находит те файлы путем чтения содержания каталога, это не пытается получить доступ к тем файлам вообще, который делает это более эффективным, чем решения это команды вызова как ls или stat в том списке файлов, вычисленных оболочкой.

Стандарт sh эквивалентный был бы:

set -- [*].txt *.txt
case "$1$2" in
  ('[*].txt*.txt') ;;
  (*) shift; script "$@"
esac

Проблема состоит в том, что с Bourne или оболочками POSIX, если шаблон не соответствует, он расширяется до себя. Итак, если *.txt расширяется до *.txt, Вы не знаете, является ли это, потому что существует нет .txt файл в каталоге или потому что существует один названный файл *.txt. Используя [*].txt *.txt позволяет различать между двумя.

39
27.01.2020, 19:38
  • 1
    [ -f /*.txt ] довольно быстро по сравнению с compgen. –  Daniel Böhmer 24.11.2016, 00:06
  • 2
    @DanielBöhmer [ -f /*.txt ] было бы неправильным, но в моем тесте на каталоге, который содержит 3425 файлы, 94 из которых нескрыты txt файлы, compgen -G "*.txt" > /dev/null 2>&1 кажись, быть с такой скоростью, как set -- *.txt; [ "$#" -gt 0 ] (20,5 секунды для обоих, когда повторено 10000 раз в моем случае). –  Stéphane Chazelas 24.11.2016, 11:51

Вы могли всегда использовать find:

find . -maxdepth 1 -type f -name "*.txt" 2>/dev/null | grep -q . && ./script

Объяснение:

  • find . : ищите текущий каталог
  • -maxdepth 1: не ищите подкаталоги
  • -type f : ищите только регулярные файлы
  • name "*.txt" : поиск файлов, заканчивающихся в .txt
  • 2>/dev/null : перенаправьте сообщения об ошибках к /dev/null
  • | grep -q . : grep для любого символа, возвратит false, если никакие символы не нашли.
  • && ./script : Выполниться ./script только если предыдущая команда была успешна (&&)
11
27.01.2020, 19:38
  • 1
    find только возвращает false, если он испытывает затруднения, ища файлы, не, если он не находит файла. Вы хотите передать вывод по каналу к grep -q . проверять, находит ли это что-то. –  Stéphane Chazelas 13.06.2013, 19:57
  • 2
    @StephaneChazelas Вы совершенно правы, конечно. Странный, хотя, я протестировал его и это, казалось, работало. Должно быть, сделал что-то странное, потому что оно больше не делает. Когда найдет, "испытывают затруднения при нахождении файлов"? –  terdon♦ 13.06.2013, 20:10
  • 3
    @terdon, как то, когда некоторый каталог недоступен, или ошибки ввода-вывода или любая ошибка, возвращенная любым системным вызовом, который это делает. В этом случае попробуйте после chmod a-x .. –  Stéphane Chazelas 13.06.2013, 20:33

Вот один лайнер, чтобы сделать это:

$ ls
file1.pl  file2.pl

файлы существуют

$ stat -t *.pl >/dev/null 2>&1 && echo "file exists" || echo "file doesn't exist"
file exists

файлы не существуют

$ stat -t -- *.txt >/dev/null 2>&1 && echo "file exists" || echo "file don't exist"
file don't exist

Этот подход использует || и && операторы в ударе. Это "или" и "и" операторы.

Таким образом, если команда статистики возвращает a $? равняйтесь 0 затем первое echo назван, если это возвращает 1, затем второе echo назван.

возвратите результаты статистики

# a failure
$ stat -t -- *.txt >/dev/null 2>&1
$ echo "$?"
1

# a success
$ stat -t -- *.pl >/dev/null 2>&1
$ echo "$?"
0

Этот вопрос экстенсивно покрыт на stackoverflow:

7
27.01.2020, 19:38
  • 1
    Почему использование нестандартное stat когда ls -d может сделать то же? –  Stéphane Chazelas 13.06.2013, 20:03
  • 2
    я думал ls -d перечисляет каталог? Казалось, не работал, когда я просто попытался перечислить каталог с файлами в нем ls -d *.pl например. –  slm♦ 13.06.2013, 20:36
  • 3
    Можно заменить оператор слева от && ls *.txt и это будет работать также. Удостоверьтесь, что Вы отправляете stdout и stderr к /dev/null как предложено @slm. –  unxnut 13.06.2013, 20:50
  • 4
    Если Вы используете ls *.txt и нет никакого подарка файлов в рамках каталога, это возвратит a $? = 2, который будет все еще работать с, если затем, но это было одной из моих причин выбора stat ls. Я хотел 0 для успеха и 1 для отказа. –  slm♦ 13.06.2013, 20:57
  • 5
    ls -d должен перечислить каталоги вместо их содержания. Так ls -d просто делает lstat на файле, точно так же, как GNU stat делает. Какой ненулевой возврат команд статуса выхода при отказе является конкретной системой, имеет мало смысла делать предположения на них. –  Stéphane Chazelas 13.06.2013, 21:58

Просто как:

cnt=`ls \*.txt 2>/dev/null | wc -l`
if [ "$cnt" != "0" ]; then ./script fi

wc -l считает строки в расширенном wildcard.

1
27.01.2020, 19:38

Как указывает Шаселас, ваш скрипт провалится, если расширение подстановочного знака будет соответствовать более чем одному файлу.

Однако, есть трюк, который я использую ( даже мне не очень нравится), чтобы обойти:

PATTERN=(/*.txt)
if [ -f ${PATTERN[0]} ]; then
...
fi

Как это работает?

Расширение wildcard будет соответствовать массиву имён файлов, мы получим первое, если оно есть, в противном случае нулевое, если оно не совпадает.

4
27.01.2020, 19:38

если вы хотите использовать предложение if, оцените подсчет:

if (( `ls *.txt 2> /dev/null|wc -l` ));then...
-2
27.01.2020, 19:38

Возможным решением также является встроенный в Bash compgen . Эта команда возвращает все возможные совпадения для шаблона подстановки и имеет код выхода, указывающий, совпадают ли какие-либо файлы.

compgen -G "/*.text" > /dev/null && ./script

Я нашел этот вопрос, когда искал более быстрые решения.

8
27.01.2020, 19:38

Более универсальным методом является:

IFS=
shopt -s nullglob
txt=('/'*'.txt')
if [ -f "$txt" ]; then./script fi

Но этот метод работает только при наличии единственного совпадения.

0
17.06.2020, 10:59

Вы можете просто использовать команду lsи свойства символа " и ():

if [ "$( ls *the folder you want to check* | grep *any pattern* )" != "" ]; then
    : *do anything you like*
fi
0
19.07.2020, 04:04

Я нахожу этот подход довольно простым и удобочитаемым в bash, не вызывая слишком много магических башизмов:

if [[ $(ls /*.txt 2>/dev/null) != "" ]]; then./script; fi
1
09.04.2021, 07:56

Теги

Похожие вопросы