Это можно "подделать".
Роб Гриффитс в 2007 году опубликовал статью под названием Легко создать много новых папок на Macworld.com, в которой он обсуждал использование команды xargs
для чтения списка файлов для создания каталогов с помощью mkdir
.
xargs
может ссылаться на placeholder
({}
) с флагом -I
,
который содержит значение для каждого аргумента, переданного в xargs
. Вот разница между этим флагом и без него:
$ foo.txt bar.txt | xargs echo
$ => foo.txt bar.txt
$ foo.txt bar.txt | xargs -I {} echo {}
$ => foo.txt
$ => bar.txt
xargs
также способен выполнять произвольные команды оболочки с флагом sh -c
:
foo.txt bar.txt | xargs sh -c 'echo arbitrary command!'
Мы можем объединить эти концепции с mkdir -p
вместо mkdir
и концепцией в @ldx's answer, чтобы получить следующее:
$ cat files.txt | xargs -I {} sh -c 'f="{}" && mkdir -p -- "${f%/*}" && touch -- "$f"'
Эта команда в основном сопоставляет каждое имя файла в списке файлов, разделенных строками, отрезает часть файла, создает каталоги с помощью mkdir -p
и затем touch
помещает имя файла в соответствующий каталог.
Скажем, например, мой files.txt
выглядит так:
deeply/nested/foo/bar.txt
deeply/nested/baz/fiz.txt
cat files.txt
выдает deeply/nested/foo/bar.js
deeply/nested/baz/fiz. txt
deeply/nested/foo/bar.js
deeply/nested/baz/fiz.txt
передается в xargs
-I {}
, xargs
переводит каждый аргумент в свою команду, так что теперь мы имеем:
deeply/nested/foo/bar.txt
deeply/nested/baz/fiz.txt
&&
для группировки 3 команд, которые выполняются последовательно - первая команда сохраняет файл в переменной окружения (которая будет повторно использована при следующем проходе файла), используя местодержатель, который мы зарегистрировали ранее, так что теперь мы имеем:
f=deeply/nested/foo/bar.txt
f=deeply/nested/baz/fiz.txt
mkdir -p
, но нам нужно вырезать имя файла. Достаточно просто, используя '${f%/*}'
:
mkdir -p deeply/nested/foo/
mkdir -p deeply/nested/baz/
f
целиком, когда touch
:
touch deeply/nested/foo/bar.txt
touch deeply/nested/baz/fiz.txt
Закари объяснил источник проблемы.
Хотя вы можете обойти ее с помощью
tty=$(tty)
tty_without_dev=${tty#/dev/}
who | grep -v "$tty_without_dev"
Это было бы неправильно, поскольку, например, если tty - это pts/1
, вы в итоге исключите все строки, содержащие pts/10
. Некоторые реализации grep
имеют опцию -w
для поиска слов
who | grep -vw pts/1
не будет соответствовать pts/10
, потому что за pts/1
не следует символ, не являющийся словом.
Или вы можете использовать awk
для фильтрации по точному значению второго поля, например:
who | awk -v "tty=$tty_without_dev" '$2 != tty'
Если вы хотите сделать это одной командой:
{ who | awk -v "tty=$(tty<&3)" '$2 != substr(tty,6)'; } 3<&0
Оригинальный stdin дублируется на дескриптор файла 3 и восстанавливается для команды tty
.
Со страницы информации tty.
'tty' печатает имя файла терминала, подключенного к его стандартному входу . Он печатает «not a tty», если стандартный ввод не является терминалом.
Проблема в том, что в вашем примере stdin tty - это канал, а не ваш терминал.
Вы можете видеть из этого примера.
$ tty
/dev/pts/29
$ echo | tty
not a tty
Чтобы обойти это, вы можете сделать что-нибудь вроде этого.
who | grep -wv "$(ps ax | awk "\$1 == $$ {print \$2}" )"
Есть более быстрый / более эффективный способ, но он требует двух команд.
t=$(tty)
who|grep -wv "${t:5}"