Home > OS >  How do I get a substring (using sed, awk, perl, etc.) after a grep result in a one-liner in a Bash s
How do I get a substring (using sed, awk, perl, etc.) after a grep result in a one-liner in a Bash s

Time:12-31

I'm trying this, but I'm clearly doing something wrong beyond my comprehension :(

sv () { ls -la ~/releases | grep -i $1 | perl -l -ne "/($1.*$)/ && print $1"; }

I'm getting this:

$ sv blah
drwxr-xr-x  10 bob  staff   320B Dec 30 07:12 blah-1.2.3

but I would like this:

$ sv blah
blah-1.2.3

CodePudding user response:

Using shell expansion on wildcard *

touch blah-1.2.3
mkdir ~/releases    
sv() ( shopt -s nullglob; for file; do ls -d "$file"*; done )
sv bla ~/relea

Output

blah-1.2.3
/home/me/releases/

DO NOT USE ls' output for anything. ls is a tool for interactively looking at directory metadata. Any attempts at parsing ls output with code are broken. Globs are much more simple AND correct: for file in *.txt. Read http://mywiki.wooledge.org/ParsingLs

CodePudding user response:

The problems are do to code injection bugs, both into the Perl program, and into the regex literal.

See How can I process options using Perl in -n or -p mode? for ways to avoid generating Perl code from the shell.

id="$1" perl -nle'/(?:^|\/)\Q$ENV{id}/ && print $ENV{id}'
perl -snle'/^(?:^|\/)\Q$id/ && print $id' -- -id="$1"

But there are better approaches. See the other answer(s).

CodePudding user response:

If OP insists on parsing the ls output ...

$ sv () { 'ls' -la ~/releases | awk -v str="$1" 'tolower($(NF))~tolower(str) {print $(NF)}'; }
bah-1.2.3

NOTES:

  • 'ls' to disable use of any aliases
  • tolower() - wrapper for both sides of ~ to facilitate a case-insensitive match
  • $(NF)~str - if last field ($(NF)) contains the string str then ...
  • print $(NF) - print the last field ($(NF))
  • will not work correctly for files that contain white space

CodePudding user response:

One idea using find, xargs and basename:

sv () { find ~/releases -iname "*$1*" -print0 | xargs -0 -r basename -a; }

Taking for a test drive:

$ sv blah
blah-1.2.3

NOTES:

  • find / -iname - for case insensitive matching
  • the find / -print0 and xargs / -0 options are included to address names with special characters (eg, linefeeds) in the name
  • basename -a - apply basename to multiple names (eg, find passes multiple matches to xargs); alternatively: xargs -n1 -r basename to supply a single match at a time to basename
  • this will also search any subdirectories of ~/releases; this can be modified with find options -maxdepth and -mindepth as needed
  • this will match on anything with blah in the name ... files, directories, symlinks, etc; this can be modified with find tests like -type
  • Related