Как хранить файлы каталогов в массиве?
Я пытаюсь сохранить список файлов в массиве, а затем снова пропустить массив.
Ниже я получаю, когда я запускаю команду ls -ls
с консоли.
total 40
36 -rwxrwxr-x 1 amit amit 36720 2012-03-31 12:19 1.txt
4 -rwxrwxr-x 1 amit amit 1318 2012-03-31 14:49 2.txt
Следующий bash script, который я написал для хранения вышеуказанных данных в массив bash.
i=0
ls -ls | while read line
do
array[ $i ]="$line"
(( i++ ))
done
Но когда я echo $array
, я ничего не получаю!
FYI, я запускаю script следующим образом: ./bashscript.sh
Ответы
Ответ 1
Попробуйте:
#! /bin/bash
i=0
while read line
do
array[ $i ]="$line"
(( i++ ))
done < <(ls -ls)
echo ${array[1]}
В вашей версии while
выполняется в подоболочке, переменные среды, которые вы изменяете в цикле, не видны вне его.
(Имейте в виду, что синтаксический анализ вывода ls
обычно не является хорошей идеей вообще.)
Ответ 2
Я бы использовал
files=(*)
И если вам нужны данные о файле, например размер, используйте команду stat
для каждого файла.
Ответ 3
Здесь вариант, который позволяет использовать шаблон регулярного выражения для начальной фильтрации, изменить регулярное выражение, чтобы получить необходимую фильтрацию.
files=($(find -E . -type f -regex "^.*$"))
for item in ${files[*]}
do
printf " %s\n" $item
done
Ответ 4
Это может сработать для вас:
OIFS=$IFS; IFS=$'\n'; array=($(ls -ls)); IFS=$OIFS; echo "${array[1]}"
Ответ 5
Выполнение любой команды оболочки внутри $(...)
поможет сохранить выходные данные в переменной. Таким образом, используя это, мы можем конвертировать файлы в массив с помощью IFS
.
IFS=' ' read -r -a array <<< $(ls /path/to/dir)
Ответ 6
Здесь более надежный подход для перебора результатов списка каталогов. Скорее всего, это смысл хранить его в массиве (в соответствии с поставленным вопросом).
Первоначально я тоже хотел получить массив результатов ls
, которые я мог бы повторить. Но мой скрипт-прерыватель не поддерживает массивы! На самом деле часто /bin/sh
не поддерживает: https://unix.stackexchange.com/info/137566/arrays-in-unix-bourne-shell.
Чтобы решить эту проблему, я написал следующую функцию с методологией "callback"/"function pointer-esque" для общего использования. Я надеюсь, что этот код будет функционировать в большинстве интерпретаторов/дистрибутивов Linux. Самая большая зависимость - это awk
, но это почти повсеместно...
Несмотря на то, что вы можете отключить весь код, обратите внимание, это действительно предназначено для того, чтобы быть скрытым в сценарии, который вы "вставляете" в другие как часть "библиотеки". Реализация функции короткая и приятная, обеспечивая при этом массу энергии.
Исходный код
# Fetch a directory list. Callback into a function with each item.
withLs()
{
# required arguments
local usage='Usage: withLs path func [lsOpt] [pass thru values]'
local path=$1; local func=$2;
if [ -z "${path}" ] || [ -z "${func}" ] ; then
echo ${usage} >&2
return 1;
fi
# optional ls arguments (respect a convenient "discard argument" option)
local lsOpt=$3
if [ "${lsOpt}" == "-" ]; then lsOpt=""; fi
# *all* remaining arguments are gathered into one space delimited variable
local passThru=$(echo "[email protected]" | awk '{$1=$2=$3="\b"; printf $0}')
# get the directory from the input path
inputDirPath=$(dirname "${path}")
if [ ! -d "${inputDirPath}" ] ; then inputDirPath=${PWD}; fi
# define / manage delimiters & "Internal Field Separator" (IFS)
local DELIM=';'
local NATURAL_IFS=' '
local OIFS=$IFS
IFS=${NATURAL_IFS} # enforce this so ls & awk work together as expected
# Fetch the ls results into a semi-colon delimited string via black magic
# (i.e. fancy awk scripts). The alternate delimiter retains spaces in
# paths. Discard stderr messages for invalid paths, so they simply produce
# no results, allowing this function to fail gracefully.
local dirList
if [ "${lsOpt/R/}" != "${lsOpt}" ]; then
# recursive
dirList=$(ls -${lsOpt} ${path} 2>/dev/null |
awk -v d=${DELIM} '
/:$/&&f{s=$0;f=0}
/:$/&&!f{sub(/:$/,"");s=$0;f=1;next}
NF&&f{printf s"/%s"d,$0}' )
else
# non recursive (forced when an * may imply recursive)
if [ "${path/\*/}" != "${path}" ]; then lsOpt=d${lsOpt}; fi
dirList=$(ls -1${lsOpt} ${path} 2>/dev/null |
awk -v d=${DELIM} '{printf "%s"d,$0}')
fi
# change the "internal field separator" and iterate over the items
IFS=${DELIM}
for item in ${dirList}; do
if [ ! -z "${item}" ] && # skip any empty or dot items encountered
[ "${item}" != "." ] && [ "${item}" != ".." ] ; then
# get the parent directory & base file/dir name for each item
entry=$(basename "${item}")
dirPath=$(dirname "${item}")
if [ ! -d "${dirPath}" ] ; then dirPath=${inputDirPath}; fi
if [ "${dirPath}" == "." ]; then dirPath="${PWD}"; fi
if [ "${dirPath}" != "/" ]; then dirPath="${dirPath}/"; fi
# invoke the call back!
${func} "${dirPath}" "${entry}" ${passThru}
fi
done
# restore the original IFS
IFS=$OIFS
}
Базовый пример
Вот пример того, как это реализовано:
Сначала определите функцию для обратного вызова:
print(){ echo "dir: $1, item: $2"; }
Затем вызовите withLs
:
withLs / print
В этом случае я печатаю каждый элемент в корневом каталоге. Функция print
вызывается для каждого элемента в списке, получая путь к каталогу в качестве аргумента $1
и элемент списка в качестве аргумента $2
(примечание $1
заканчивается косой чертой, поэтому $1$2
является полным дорожка). Когда я вызываю withLs
, я передаю путь для использования с ls
для извлечения списка и имя моей функции обратного вызова.
Wildcards
Моя функция поддерживает символы подстановки, но, к сожалению, сценарии оболочки могут приводить к появлению звездочек *
в аргументах, если вы не будете осторожны. Если вам нужна звездочка в пути, вы должны избегать ее при вызове функции. Вы можете сделать это, заключив его в кавычки или добавив обратную косую черту \
. Поскольку цитирование других магических символов, таких как тильда ~
(т.е. сокращение домашнего каталога), может вызвать проблемы, я рекомендую использовать обратную косую черту. Если вы хотите напечатать элементы в вашем домашнем каталоге с расширением .sh
, как вы можете:
withLs ~/\*.sh print
Опции для ls
- например, Рекурсия
При желании можно также передать третий аргумент lsOpt
, который может содержать любые надстройки для ls, такие как a
, для вывода списка скрытых файлов. Таким образом, чтобы распечатать все элементы в домашнем каталоге, в том числе скрытые...
withLs ~ print a
Еще одна мощная опция для ls - это рекурсия, обозначенная буквой "R", например:
withLs ~ print R
При использовании этого режима обратите внимание, что ваша функция обратного вызова будет передавать родительский каталог каждому элементу, так что $1$2
по-прежнему соответствует фактическому полному пути (т.е. $1
может быть дочерним по отношению к каталогу верхнего уровня, в котором выполняется поиск).
Чтобы объединить опции ls
, просто запустите их вместе как одну строку, например Ra
.
Передайте значения
Наконец, я предоставил открытые значения "пройти через". То есть предоставить другие пользовательские аргументы в ваш обратный вызов. В этом случае print получит дополнительные аргументы "foo" и "bar":
print(){ echo "dir: $1, item: $2, passThru: $3 $4 $5 $6"; }
withLs ~ print - foo bar
Если вы хотите использовать функцию pass thru, но не заботитесь о третьем аргументе (то есть опциях ls), вы можете передать "-" в качестве "заполнителя" (как показано выше).