Когда такой шаблон, как abc*
, используется в оболочке без кавычек, оболочка попытается сопоставить его с доступными именами файлов (это называется «генерация имени файла», но часто упоминается как «подстановка» ). Если он не соответствует какому-либо имени файла, большинствоsh
-подобных оболочек оставят шаблон нераскрытым и передают его утилите как есть.
Пример:
$ touch xyz
$ touch abc*
$ tree
.
|-- abc*
`-- xyz
0 directory, 2 files
$ touch xyz*
$ tree
.
|-- abc*
`-- xyz
0 directory, 2 files
Команда touch xyz*
не не создала файл с именем xyz*
, поскольку имя файла xyz
соответствовало шаблону. Поэтому утилита touch
была вызвана с именем файла xyz
.
В оболочке bash
установка параметра оболочки failglob
с shopt -s failglob
заставит оболочку жаловаться, если глобус оболочки ничему не соответствует:
$ shopt -s failglob
$ touch 123*
bash: no match: 123*
Эквивалентная опция включена по умолчанию в оболочке zsh
.
Установка параметра оболочки nullglob
вbash
(или NULL_GLOB
вzsh
)приведет к исчезновению шаблона, если он не соответствует имени файла:
$ shopt -s nullglob
$ touch fo*
usage: touch [-acm] [-d ccyy-mm-ddTHH:MM:SS[.frac][Z]] [-r file]
[-t [[cc]yy]mmddHHMM[.SS]] file...
(мы получаем ошибку от touch
, так как она была вызвана без каких-либо аргументов)
Чтобы гарантировать, что шаблон используется как строка (, поскольку он ), а не используется для подстановки, вы должны заключить его в кавычки:
$ touch "file*"
$ touch "file**"
$ touch "file***"
$ tree
.
|-- file*
|-- file**
`-- file***
0 directory, 3 files
Отсутствие кавычек в этом примере дало бы вам только один файл с именем file*
(, если каталог изначально был пустым ), поскольку шаблоны file**
и file***
соответствуют этому первому имени file*
.
Работайте с ним шаг за шагом:
$ ls -lt *.txt
-rw-r--r-- 1 stew stew 18 Feb 5 19:53 file3.txt
-rw-r--r-- 1 stew stew 18 Feb 5 19:53 file2.txt
-rw-r--r-- 1 stew stew 18 Feb 5 19:53 file1.txt
У нас есть три файла, а вам нужны только два. Давайте tail
это:
$ ls -lt *.txt | tail -n 2
-rw-r--r-- 1 stew stew 18 Feb 5 19:53 file2.txt
-rw-r--r-- 1 stew stew 18 Feb 5 19:53 file1.txt
Хорошо, это хорошо, но нам нужны только два имени файла. ls -l
не тот инструмент, мы можем просто использоватьls
:
$ ls -t *.txt | tail -n 2
file2.txt
file1.txt
Затем поместите этот вывод в cat
в качестве аргументов с$()
:
$ cat $(ls -t *.txt | tail -n 2)
content of file 2
content of file 1
Вы спрашивали, как использовать псевдоним для ls -lthr
. -l
не очень хорошо работает, потому что печатает не только имена файлов. -h
имеет смысл только при наличии -l
. Итак, вот пример дляls -tr
:
$ alias lt='ls -tr'
$ cat $(lt *.txt | tail -n 2)
content of file 2
content of file 3
Если в вашем .bashrc
есть что-то вроде alias ls='ls -lthr'
, вы можете использовать command ls
или env ls
. Если вам нужно что-то еще, что обрабатывает нечетные символы (, такие как символы новой строки )в файлах, вот пример использования find
вместо:
cat $(find. -name '*.txt' | tail -n 2)
Однако порядок может быть не таким, как в вашем ls
решении, и оно также будет выполнять поиск в подкаталогах.
Вы можете просто выполнить следующую команду в одной строке:
$ ls *.txt | tail -2 | xargs cat
Пояснение:
Создайте три тестовых файла:
tc@user1:~$ echo "first file reads 1" > file1.txt
tc@user1:~$ echo "second file reads 2" > file2.txt
tc@user1:~$ echo "third file reads 3" > file3.txt
Убедитесь, что файлы созданы:
tc@user1:~$ ls -l
total 12
-rw-r--r-- 1 tc staff 19 Feb 5 18:58 file1.txt
-rw-r--r-- 1 tc staff 20 Feb 5 18:58 file2.txt
-rw-r--r-- 1 tc staff 19 Feb 5 18:58 file3.txt
Список всех текстовых файлов, затем чтение содержимого последних двух файлов с использованием конвейера:
tc@user1:~$ ls *.txt | tail -2 | xargs cat
second file reads 2
third file reads 3
Пока у вас есть GNU coreutils ls:
eval "sorted=( $(ls -rt --quoting-style=shell-escape *.txt) )"
cat "${sorted[@]:0:3}"
Это не идеальное решение, но оно создаст массив с текстовыми файлами в вашем текущем каталоге, отсортированными по времени изменения, а затем выделит первые 3.
Чтобы правильно обрабатывать все возможные имена файлов (, включая имена с символами новой строки ), следующий вызов будет вызывать cat
для двух наименее недавно измененных файлов, при этом самый старый файл обрабатывается последним, используя оболочку zsh
:
cat./*.txt(.om[-2,-1])
Следующее будет cat
двумя последними измененными файлами, причем последний измененный будет обрабатываться первым:
cat./*.txt(.om[1,2])
Здесь (.om[1,2])
после шаблона подстановки ./*.txt
является квалификатором подстановки . .
в квалификаторе заставляет шаблон соответствовать только простым файлам (, а не каталогам и т. д. ). om
упорядочивает файлы по метке времени модификации (Om
, изменит порядок ). [1,2]
выбирает только первые два элемента результирующего списка. Отрицательные индексы здесь будут считаться с конца списка.
Из оболочки bash
, используя zsh
как любую другую утилиту:
zsh -c 'cat./*.txt(.om[-2,-1])'
и
zsh -c 'cat./*.txt(.om[1,2])'
соответственно.
Я предлагаю это:
arr=("$(find ~+ -type f -name "*.txt" -printf "%T@\000%p\n" | sort -nr | head -n 2 | cut -d '' -f2)")
printf "%s\n" "${arr[@]}"
# create an array from the command
arr=("$( \
# use `find` to fetch the files and the timstamp
# using null as separator
find ~+ -type f -name "*.txt" -printf "%T@\000%p\n" | \
# sort
sort -nr | \
# retrieve the first 2 files
head -n 2 | \
# retrieve only the file names
cut -d '' -f2 \
)")
# print the array
printf "%s\n" "${arr[@]}"