2011-05-04 5 views
75

В Bash я хотел бы создать функцию, которая возвращает имя файла с самым новым файлом, который соответствует определенному шаблону. Например, у меня есть каталог файлов вроде:Функция Bash для поиска новейшего шаблона сопоставления файлов

Directory/ 
    a1.1_5_1 
    a1.2_1_4 
    b2.1_0 
    b2.2_3_4 
    b2.3_2_0 

Я хочу, чтобы новый файл начинался с 'b2'. Как это сделать в bash? Мне нужно иметь это в моем сценарии ~/.bash_profile.

+1

см. Http://superuser.com/questions/294161/unix-linux-find-and-sort-by-date-modified для получения дополнительных ответных подсказок. Сортировка - это ключевой шаг для получения вашего новейшего файла. –

ответ

139

Команда ls имеет параметр -t для сортировки по времени. Затем вы можете взять первый (самый новый) с head -1.

ls -t b2* | head -1 

Но будьте осторожны: Why you shouldn't parse the output of ls

Мое личное мнение: разбор ls только опасно, когда имена файлов могут содержать забавные символы, такие как пробелы или символы новой строки. Если вы можете гарантировать, что имена файлов не будут содержать забавные символы, тогда синтаксический анализ ls вполне безопасен.

Если вы разрабатываете скрипт, который должен использоваться многими людьми во многих системах во многих разных ситуациях, то я очень рекомендую не разбирать ls.

Вот как сделать это «право»:. How can I find the latest (newest, earliest, oldest) file in a directory?

unset -v latest 
for file in "$dir"/*; do 
    [[ $file -nt $latest ]] && latest=$file 
done 
+5

. Примечание для других: если вы делаете это для каталога, вы должны добавить параметр -d в ls, как этот 'ls -td | head -1 ' –

+4

Ссылка [parsing LS] (http://mywiki.wooledge.org/ParsingLs) говорит, что не делать этого и рекомендует методы в [BashFAQ 99] (http://mywiki.wooledge.org/BashFAQ/099). Я ищу 1-лайнер, а не что-то пуленепробиваемое, чтобы включить его в скрипт, поэтому я продолжу разбирать ls, не похожий на @lesmana. – Eponymous

+0

@Eponymous: Если вы ищете один вкладыш без использования хрупких 'ls',' printf "% s \ n" b2 * | голова -1' сделает это за вас. –

2

Необычные имена файлов (например, файл, содержащий действительный \n характер может нанести ущерб с такого рода разборе Вот способ сделать это в Perl:

perl -le '@sorted = map {$_->[0]} 
        sort {$a->[1] <=> $b->[1]} 
        map {[$_, -M $_]} 
        @ARGV; 
      print $sorted[0] 
' b2* 

Это Schwartzian transform там используется

+0

Пусть шварц будет с вами! –

+0

этот ответ может работать, но я не доверяю ему, учитывая плохую документацию. –

+1

плохой документации? он поставляется с wikipage ... –

4

Это возможная реализация. искомая функция Bash:

# Print the newest file, if any, matching the given pattern 
# Example usage: 
# newest_matching_file 'b2*' 
# WARNING: Files whose names begin with a dot will not be checked 
function newest_matching_file 
{ 
    # Use ${1-} instead of $1 in case 'nounset' is set 
    local -r glob_pattern=${1-} 

    if (($# != 1)) ; then 
     echo 'usage: newest_matching_file GLOB_PATTERN' >&2 
     return 1 
    fi 

    # To avoid printing garbage if no files match the pattern, set 
    # 'nullglob' if necessary 
    local -i need_to_unset_nullglob=0 
    if [[ ":$BASHOPTS:" != *:nullglob:* ]] ; then 
     shopt -s nullglob 
     need_to_unset_nullglob=1 
    fi 

    newest_file= 
    for file in $glob_pattern ; do 
     [[ -z $newest_file || $file -nt $newest_file ]] \ 
      && newest_file=$file 
    done 

    # To avoid unexpected behaviour elsewhere, unset nullglob if it was 
    # set by this function 
    ((need_to_unset_nullglob)) && shopt -u nullglob 

    # Use printf instead of echo in case the file name begins with '-' 
    [[ -n $newest_file ]] && printf '%s\n' "$newest_file" 

    return 0 
} 

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

+0

Вы могли бы использовать «nullglob_shopt = $ (shopt -p nullglob)», а затем «$ nullglob», чтобы вернуть «nullglob», как это было раньше. –

+0

Предложение от @gniourf_gniourf использовать $ (shopt -p nullglob) является хорошим. Обычно я стараюсь избегать использования подстановки команд ('$()' или backticks), потому что она медленная, особенно в Cygwin, даже когда команда использует встроенные функции. Кроме того, контекст подоболочки, в котором команды запускаются, может иногда приводить к непредсказуемым действиям. Я также стараюсь избегать хранения команд в переменных (например, «nullglob_shopt»), потому что очень плохое может случиться, если вы ошиблись в значении переменной. – pjh

+0

Я ценю внимание к деталям, которые могут привести к неясной ошибке при пропуске. Благодаря! –

2

Существует гораздо более эффективный способ достижения этого. Рассмотрим следующую команду:

find . -cmin 1 -name "b2*" 

Эта команда находит последнюю версию файла производится ровно одна минута назад с подстановочные поиск по «b2 *». Если вам нужны файлы за последние два дня, вам будет лучше с помощью приведенной ниже команды:

find . -mtime 2 -name "b2*" 

"." представляет текущий каталог. Надеюсь, это поможет.

+5

На самом деле это не находит «новейший шаблон соответствия файла» ... он просто находит все файлы, соответствующие шаблону, созданному минуту назад или изменен два дня назад. – GnP

+0

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

2

Сочетание find и ls хорошо работает для

  • имена файлов без переводов строк
  • не очень большое количество файлов
  • не очень длинные имена файлов

Решение:

find . -name "my-pattern" ... -print | 
    xargs -0 ls -1 -t | 
    head -1 

Давайте разбить его:

С find мы можем сопрягать все интересные файлы, как это:

find . -name "my-pattern" ... 

затем с помощью -print0 мы можем передать все имена файлов безопасно для ls как это:

find . -name "my-pattern" ... -print0 | xargs -0 ls -1 -t 

ls -t будет сортировать файлы по времени модификации (новейшим первым) и распечатать его по одной строке. Вы можете использовать -c для сортировки по времени создания. Примечание: это сломается с именами, содержащими символы новой строки.

Наконец head -1 доставит нам первый файл в отсортированном списке.

Примечание:xargs использовать системные ограничения для размера списка аргументов. Если этот размер превышает, xargs будет называть ls несколько раз. Это нарушит сортировку и, возможно, также окончательный вывод. Пробег

xargs --show-limits 

, чтобы проверить пределы вашей системы.

Смежные вопросы