Список файлов в скрипте оболочки - PullRequest
7 голосов
/ 30 марта 2012

Когда я пытаюсь использовать приведенный ниже код, я получаю все файлы, имя файла которых начинается с E

#!/bin/bash

data=$(ls -trh E*)
for entry in ${data}
do
  echo ${entry}
done

Но если я попробую приведенный ниже код, который получает подстановочный знак из аргумента, я получаю только первое имя файла

#!/bin/bash

data=$(ls -trh $1)
for entry in ${data}
do
  echo ${entry}
done

Может кто-нибудь помочь мне решить эту проблему ..

Когда я давал кавычки, подобные этому myscript.sh 'E *', он работал нормально, есть ли способ сделать это без кавычек

Ответы [ 6 ]

7 голосов
/ 30 марта 2012

Это проблема расширения оболочки.

Ваша оболочка будет интерпретировать символы подстановки, прежде чем перейти к вашему процессу.Например:

script.sh

#!/bin/bash
echo $1 $2 $3

Запуск вышеуказанного с подстановочным знаком:

> ./script.sh E*
Eva   Eve   Evolve

Если вы хотите передать аргументбез интерпретации оболочки сначала вам нужно будет процитировать его:

> ./script.sh 'E*'
E*

Лучшее решение с использованием find:

Что вы на самом деле пытаетесь сделать, это получить список всех файлови папки в данном каталоге, в обратном порядке изменения времени (сначала самые старые).Вывод ls общеизвестно больно анализировать.Вместо этого предпочтительнее использовать мощную и гибкую команду find.

Это одна строка:

>  find ./ -maxdepth 1 -printf "%A@ %f\0" | sort -z -n | while read -d '' date line; do echo "$line"; done

Что, вероятно, загадочно, но имеет смысл однажды объяснить.

  • Найти все файлы в этом каталоге без рекурсии find ./ -maxdepth 1
  • Для каждого файла выведите их время последнего изменения в секунду -printf "%A@
  • и их имя файла, разделенныенулевые символы %f\0"
  • Сортировать строки, разделенные нулями, по времени последнего изменения (численно) sort -z -n
  • Для каждой строки, разделенной нулями, присвойте метку времени 'date', а остальныестрока в 'line': while read -d '' date line
  • Напечатайте строку echo "$line"

Например, в моем каталоге:

> ls -l
total 4296
drwxr-xr-x 2 bfer cvs    4096 2012-03-05 15:49 colortry
drwxr-xr-x 3 bfer cvs    4096 2012-03-27 15:05 githug
drwxr-xr-x 3 bfer cvs    4096 2012-03-12 17:18 hooks-bare
drwxr-xr-x 3 bfer cvs    4096 2012-03-28 12:38 java
-rw-r--r-- 1 bfer cvs 4025413 2012-03-27 12:53 mozdebug
drwxr-xr-x 2 bfer cvs    4096 2012-02-16 12:54 mustache_bug_demo
-rwxr-xr-x 1 bfer cvs     113 2012-03-30 12:20 try.sh
> find ./ -maxdepth 1 -printf "%A@ %f\0" | sort -z -n | while read -d '' date line; do echo "$line"; done
mozdebug
colortry
hooks-bare
mustache_bug_demo
githug
java
try.sh
./

Если вы не 'Чтобы получить результат ./, просто извлеките его из окончательного набора.

обновлено: С предложением Сорпигала обрабатывать имена файлов с символами новой строки.

Дальнейшее примечание по расширению оболочки

То же самое относится и к скобками амперсанды.Например, следующие команды curl не будут работать должным образом:

> curl example.com/site?q=hello&name=bob
> echo 23/(7/98) | bc

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

Длячтобы это работало правильно, вам нужно будет процитировать аргументы:

> curl "example.com/site?q=hello&name=bob"
> echo "23/(7/98)" | bc
2 голосов
/ 30 марта 2012

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

./script "E*"

Цитирование будет предотвращать расширение оболочки.

2 голосов
/ 30 марта 2012

Вы указали свой шаблон при вызове сценария?

bash yourscript.sh 'E*'

В противном случае ваш скрипт получит вещи, которые начинаются с E в вашем текущем каталоге, и $ 1 становится точно первым файлом, таким образом, поведение

1 голос
/ 30 марта 2012

Вы не должны использовать ls в сценарии оболочки.Вывод ls предназначен для потребления человеком * только 1003 * и никогда не должен использоваться в качестве ввода для другой команды.

Во-первых, правильное решение вашей проблемы.

#!/usr/bin/env bash
pat="$1"
while IFS= read -r -d '' file ; do
    echo "$file"
done < <(find . -maxdepth 1 -name "$pat" -print0)

Это предполагает, что у вас нет особых требований к порядку или формату имени.Вы можете сослаться на этот ответ , если вам нужно сделать заказ, например ls -t.

Ваша проблема распространена, и я называю это "Кто что видит?"проблема.Вы говорите myscript.sh E* и ожидаете, что литерал E* будет передан в ваш скрипт, но на самом деле, поскольку этот аргумент не заключен в кавычки, glob будет расширен на bash до вызова вашего скрипта, такваш скрипт видит все файлы в текущем каталоге, имена которых начинаются с E.

. Как вы заметили, аргумент "в кавычках" исправляет это.Это связано с тем, что bash не выполняет какого-либо специального расширения внутри одинарных кавычек, поэтому теперь ваш сценарий видит E* буквально.Вы фактически избежали *, поэтому bash не расширит его, пока не передаст его в сценарий.Сделать это без использования кавычек можно с помощью обратного слэша-экранирования *

myscript.sh E\*

Но лучшим решением будет позволить bash расширить глобус и изменить скрипт для обработки нескольких аргументов файла.,Например:

#!/bin/bash

data=("$(ls -trh "$@")")
for entry in "${data[@]}"
do
  echo "${entry}"
done

Здесь я предполагаю, что все аргументы будут именами файлов.Я изменил data в массив и добавил кавычки для всех расширений, чтобы правильно сохранить пробелы.Это решение по-прежнему неверно, потому что оно анализирует ls выходные данные, но теперь вы, вероятно, получаете ожидаемое поведение без кавычек E*

myscript.sh E*

В общем случае вы должны всегда цитируйте расширение переменных в сценариях оболочки, потому что это, вероятно, делает то, что вы ожидаете (а расширение без кавычек, вероятно, делает , а не !)

0 голосов
/ 19 января 2013

Вот краткий пример:

найти файлы каталога, упорядоченные по датам их модификации от более старых к более новым, но меньшим или равным заданной дате и содержащие в именах «.inc»

"find-files.sh"
#!/bin/bash
store=$1
date_given=$2
inc_files=()
limit=$( date +"%s" -d "$date_given" )
while read -r -d $'\0' seconds file; do
   seconds=${seconds%.*}
   if [[ $seconds -le $limit ]]; then
      printf "seconds=$seconds, file=$file\n"
      #block word splitting
      inc_files=( "${inc_files[@]}" "$file" )
      #do whatever you want here...
   fi
done < <(find $store -maxdepth 1 -type f -name "*.inc*" -printf "%T@ %f\0" | sort -zn)

и назовите его:

./find-files.sh '/a/dir/to/search' '2013-01-12 18:12:09'

Я согласен с @Sorpigal https://unix.stackexchange.com/questions/22674/shell-script-for-moving-oldest-files/29205#29205, это очень полезно, кроме 'IFS =' в цикле while, который разбивает всемоя система (Ubuntu).

0 голосов
/ 30 марта 2012

Попробуйте это.

#!/bin/bash
data=$(ls -trh "$@" | cat -E)
while [ ${#data} != 0 ]
do
  echo ${data%%\$*}
  data=${data#*\$}
done
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...