Как сопоставить строку в файле с помощью чистого bash?

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 ).

0
23.03.2021, 20:35
1 ответ

Вы можете сделать это. Но это очень, очень плохая идея. Он будет намного медленнее (, так как на порядки медленнее ), чем 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 МБ. Надеюсь, вы видите проблему...

4
28.04.2021, 22:57

Теги

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