Let’s split those for explanations.
sed -n '/^\s*NEEDED\s/{s/^\s*NEEDED\s*\(.*\)\.so.*/lib:\1/;s/-/_/;p}
:
-n # no automatic print
/regex/ {...} # apply block to lines selected by /regex/:
^ # anchor at the beginning
\s* # any number of whitespaces
NEEDED # literal string
\s # one whitespace
s/X/Y/ # substitute X for Y
# it will match the whole line,
# save whatever is between the spaces and '.so',
# and prepend 'lib:' to the saved part
X:
^ # anchor at the beginning
\s* # any number of whitespaces
NEEDED # literal string
\s* # any number of whitespaces
\( # start of capture group
.* # any number of characters
\) # end of capture group
\. # literal dot
so # literal string
.* # any number of characters
Y:
lib: # literal lib:
\1 # the first capture group
; # command separator
s/-/_/; # substitute '-' for '_'
# BUG ALERT!!!
# This only substitutes the first match. It should be:
# y/-/_/;
p # print
The other one, sed -n 's/^\s*NEEDED\s*//p'
just selects the NEEDED lines and removes that part (substitutes it for nothing), leaving just the library name. Like your grep and cut, but in one process and without having to count spaces.
May I also hijack the thread for some tips and transform your script into something like my one-liner?
objdump -x $1 |grep NEEDED | cut -d " " -f 18 - > $tmpfile1
You can tell by now that I prefer not to count and use one program instead of two:
objdump -x "$1" | sed -n 's/^\s*NEEDED\s*//p' > "${tmpfile1}"
Also notice argument quoting. That allows us to take a filename with spaces in it.
for i in $(cat $tmpfile1); do
locate -b -e -l 1 $i | sort |grep /boot/system/develop/lib >> $tmpfile2
locate -b -e -l 1 $i | sort | grep /boot/system/lib/ >> $tmpfile2
done
You are telling locate
to only output one match with -l 1
, so there’s no point in sorting it. Even if you weren’t, it’s better to sort after grep: there’d be less stuff to sort. In fact, you don’t want to limit it, as the first match may not be in the directories you want.
You can pass several filters to grep, halving the processes launched. You can also build a regex to match everything, but let’s leave that for another life. You’ll also want to anchor the match to the beginning of the line, just in case you have those as part of a deep path somewhere.
for i in $(cat "${tmpfile1}"); do
locate -b -e $i | grep -e '^/boot/system/develop/lib' -e '^/boot/system/lib/' >> "${tmpfile2}"
done
I’ve left sort
out of here for the moment. There’s no point in sorting the results for each of the libraries.
You can further reduce the number of processes by grepping the result of the whole loop instead of every iteration:
for i in $(cat $tmpfile1); do
locate -b -e $i
done | grep -e '^/boot/system/develop/lib' -e '^/boot/system/lib/' >> $tmpfile2
Extreme example of impact:
> time for f in *; do echo $f; done | grep -e w -e x
...
real 0m0,006s
user 0m0,006s
sys 0m0,003s
> time for f in *; do echo $f | sort | grep w; echo $f | sort | grep x; done
...
real 0m0,369s
user 0m0,139s
sys 0m0,448s
You may even be able to directly tell locate
to search in those directories. But then I’ve not used locate
or find
in Haiku to look for full filenames since I learnt about query
. Why keep a database updated or traverse the filesystem when it already has an index? So let’s change locate -b -e $i
into query -a name="$i"
and get rid of updatedb
, which, by the way you left running in the background, so those locate
processes may or not get data from an updated database. Quoting (notice I used "$i"
instead of $i
) will help with “spaces in the filenames of results” problems. Not yet, due to how you are looping, but we’ll get to that.
cat $tmpfile3 | uniq
Even with your previous sorts, the data in $tmpfile
is not sorted, and duplicates may be sprinkled all around:
cat $tmpfile3 | sort | uniq
Speaking of which, all those dumping to a file to just cat
it and delete it are useless in the final script. As you are not reading from them several times or anything like that, you can avoid creating a file, writing to it, launching a process and reading from it. For example, instead of splitting the output of cat $tmpfile1
and looping it, just do so with the output of objdump
:
for i in $(objdump -x "$1" | sed -n 's/^\s*NEEDED\s*//p'); do
Better yet, pipe it and read it line by line, thus not splitting by space.
objdump -x "$1" | sed -n 's/^\s*NEEDED\s*//p' | while read -r i; do
You could also change the IFS
environment variable, but I won’t try that, although we could use it to launch just one catattr
.
After all of that, we get to this script:
#!/bin/sh
if [ $# -ne 1 -o "$1" == "--help" ]; then
echo "Usage: findpkgs <executable_filename>"
echo " and no newlines in the filenames, please."
exit 1
fi
objdump -x "$1" | sed -n 's/^\s*NEEDED\s*//p' | while read -r library; do
query -a name="${library}"
done | grep -e '^/boot/system/develop/lib' -e '^/boot/system/lib/' | sort | uniq | while read -r file; do
catattr -d SYS:PACKAGE_FILE "${file}"
done | sort | uniq