Передача вывода одной команды другой

Когда такой шаблон, как 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*.

0
05.02.2021, 20:48
5 ответов

Работайте с ним шаг за шагом:

$ 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решении, и оно также будет выполнять поиск в подкаталогах.

0
18.03.2021, 22:32

Вы можете просто выполнить следующую команду в одной строке:

$ 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
0
18.03.2021, 22:32

Пока у вас есть GNU coreutils ls:

eval "sorted=( $(ls -rt --quoting-style=shell-escape *.txt) )"
cat "${sorted[@]:0:3}"

Это не идеальное решение, но оно создаст массив с текстовыми файлами в вашем текущем каталоге, отсортированными по времени изменения, а затем выделит первые 3.

-1
18.03.2021, 22:32

Чтобы правильно обрабатывать все возможные имена файлов (, включая имена с символами новой строки ), следующий вызов будет вызывать 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])'

соответственно.

2
18.03.2021, 22:32

Я предлагаю это:

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[@]}"
-1
18.03.2021, 22:32

Теги

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