Now, I need to know when blabla is called using the shebang, or not:
В C вы можете получить эту информацию через
getauxval(AT_EXECFN)
, которая сообщит вам имя исходного исполняемого файла (, то есть первый аргумент, переданный вexecve(2)
)[1].Но эта строка помещается в память сразу после аргументов командной строки и строк среды, в конце
[stack]
области памяти, поэтому ее можно получить непосредственно оттуда.Например, следующий perl-скрипт (назовите его
foo.pl
), если сделать его исполняемым сchmod 755 foo.pl
, будет напечатано./foo.pl
при прямом запуске и/usr/bin/perl
при запуске какperl./foo.pl
:#! /usr/bin/perl open my $maps, "/proc/self/maps" or die "open /proc/self/maps: $!"; my $se; while(<$maps>){ $se = hex($1), last if /^\w+-(\w+).*\[stack\]$/ } open my $mem, "/proc/self/mem" or die "open /proc/self/mem: $!"; sysseek $mem, $se - 512, 0; sysread $mem, $d, 512 or die "sysread: $!"; print $d =~ /([^\0]+)\0+$/, "\n";
В более новых ядрах (>=3.5 )linux конец среды также доступен в
/proc/PID/stat
(в 51-м поле, как описано на справочной страницеproc(5)
).#! /usr/bin/perl open my $sh, "/proc/self/stat" or die "open /proc/self/stat: $!"; my @s = <$sh> =~ /\(.*\)|\S+/g; open my $mem, "/proc/self/mem" or die "open /proc/self/mem: $!"; seek $mem, $s[50], 0; $/ = "\0"; my $pn = <$mem> or die "readline: $!"; chomp $pn; print "$pn\n";
[1] Ядра Linux новее 2.6.26 представили запись вспомогательного вектора, указывающую на него (см. коммит ), но имя исполняемого файла было доступно в конце стека задолго до этого (, поскольку Linux -2.0 от 1996 ).
Вы можете сделать это. Но это очень, очень плохая идея. Он будет намного медленнее (, так как на порядки медленнее ), чем grep
, и менее переносим, поскольку зависит от особенностей конкретной оболочки (Bash ).
Это напечатает строки, соответствующие шаблону регулярного выражения, указанному в качестве первого аргумента, аналогичноgrep pattern
:
#!/bin/bash -
regexp="$1"
ret=1
while IFS= read -r line || [ -n "$line" ]; do
if [[ $line =~ $regexp ]]; then
printf '%s\n' "$line"
ret=0
fi
done
exit "$ret"
Сохраните это как foo.bash
и запустите так:
foo.bash pattern < inputFile
Или используя стандартный sh
синтаксис, ища фиксированную строку, а не регулярное выражение:
#!/bin/sh -
string="$1"
ret=1
while IFS= read -r line || [ -n "$line" ]; do
case $line in
(*"$string"*)
printf '%s\n' "$string"
ret=0
esac
done
exit "$ret"
(Замените printf
на exit 0
, чтобы получить поведение, подобное grep -q
.)
Просто чтобы дать вам представление о том, насколько это медленно, я создал файл всего с 10001 строкой, первые 5000 строк foo
, затем одна bar
, а затем еще 5000 foo
:
perl -e 'print "foo\n" x 5000; print "bar\n"; print "foo\n" x 5000;' > file
Теперь сравните время для grep
и скрипта выше:
$ time grep bar < file
bar
real 0m0.002s
user 0m0.002s
sys 0m0.000s
$ time./foo.bash bar < file
bar
real 0m0.116s
user 0m0.101s
sys 0m0.016s
Как видите, даже с этим крошечным файлом разница заметна. Если мы попробуем с более существенным, то время, когда сценарий сменяется, становится почти невыносимым :
.$ perl -e 'print "foo\n" x 500000; print "bar\n"; print "foo\n" x 500000;' > file
$ time grep bar < file
bar
real 0m0.004s
user 0m0.000s
sys 0m0.004s
$ time./foo.bash bar < file
bar
real 0m11.306s
user 0m10.117s
sys 0m1.188s
Однако отчасти это связано с тем, что Bash работает медленно. Стандартная версия sh работает немного быстрее с Dash :
.$ time dash foo2.sh bar < file
bar
real 0m3.467s
user 0m2.113s
sys 0m1.353s
Тем не менее,это все еще разница в трех порядков. Несколько секунд для скриптов против ближайшего -мгновения grep
. И это по-прежнему файл всего с миллионом строк и размером ~4 МБ. Надеюсь, вы видите проблему...