Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Well, this apparently is not possible the way I'm trying it.

This alternate approach to obtain bar as a resulting string works, though:

#!/bin/bash

path1=/usr/tmp/foo/bar/baz

awk -F/ '{print $5}' <<< "$path1"

So far so good, but what if I want to do without the <<< operator as well as those notorious echo | ... pipes? In a nutshell, what I'm trying to do is passing path1 as a variable with the -v pa="$path1" directive and using both the field separator -F/ and the field identifiers (e. g. $5) to parse the awk-internal pa variable, which got its value assigned from the external path1 shell variable. Can this be done "inside" awk, too?

share|improve this question

4 Answers 4

up vote 6 down vote accepted

The problem with the -v option or with the var=value arguments to awk is that they can't be used for arbitrary data since ANSI C escape sequences (like \n, \b...) are expanded in them.

The alternative is to use the ARGV or ENVIRON awk arrays:

awk -F/ 'BEGIN{$0 = ARGV[1]; print $5}' "$path1"

Or:

export path1
awk -F/ 'BEGIN{$0 = ENVIRON["path1"]; print $5}'

Or:

path1="$path1" awk -F/ 'BEGIN{$0 = ENVIRON["path1"]; print $5}'

Now, if all you want is split a shell variable, you may not need awk.

In all POSIX shells:

IFS=/; set -f
set -- $path1
printf '%s\n' "$5"
share|improve this answer
    
Thank you, this looks interesting as well! Just one thing always confuses me again: awk expects a file after the '...', right? So why does it accept a string in this case without complaining? -- To your second approach: As I normally use local variables in my bash scripts, I normally do not make them available from outside using export. And not to be forgotten: these variables will have to be un`export`ed again to reduce clutter, whilst local variables are destroyed after the script ends (or exits prematurely). –  syntaxerror Aug 19 '13 at 20:04
2  
@syntaxerror, See the 3rd variant then, or use ARGV. If there's only a BEGIN section, files or standard input are not read. –  Stéphane Chazelas Aug 19 '13 at 20:10
    
Nice, thanks, I'll remember that from now on! Besides, your logic also works the other way round: that is to say, if you do NOT set a BEGIN section and use a variable like this here (and not a file), awk may "stall" and you have to CTRL-C it to get back to your shell prompt. Didn't happen to me only once when I did my first awk steps some moons ago. –  syntaxerror Aug 19 '13 at 20:16
    
Thanks for showing how to use ENVIRON. I tried that but didn't realize to drop the $ bit from the variable name. Very nice and elegant solutions! –  slm Aug 19 '13 at 20:22

You could split pa into an array

awk -F'/' -v pa=$path1 'BEGIN{split(pa, arr, FS); print(arr[5]); exit}'
share|improve this answer
    
This is sneaky, thanks! :) –  syntaxerror Aug 19 '13 at 18:33
1  
That fails if $path1 contains blanks. May fail if it contains wildcard characters or backslashes. –  Stéphane Chazelas Aug 19 '13 at 20:11
    
@StephaneChazelas, good point. And +1 for your solution that addresses these issues –  1_CR Aug 19 '13 at 20:35

There are a couple of ways you can pass environment variables into an awk script/command:

Method #1

This gets the shell to expand the variable, $path1, prior to running the awk command.

$ echo $path1
/usr/tmp/foo/bar/baz
$ awk -F'/' 'END{a="'$path1'"; split(a,arr,FS); print(arr[5])}' /dev/null
bar

Method #2

Pass env. variable in as a awk variable, a.

$ awk -F '/' 'END{split(a,arr,FS); print(arr[5])}' a=$path1 /dev/null
bar

Method #3

Explicitly pass awk a variable using the -v switch.

$ awk -F'/' -v a=$path1 'END{split(a,arr,FS); print(arr[5])}' /dev/null
bar

Debugging

You can enable the shell's verbosity by setting set -x before running any of these commands to see what's going on. Here's method #2 as an example:

$ set -x
$ awk -F '/' 'END{split(a,arr,FS); print(arr[5])}' a=$path1 /dev/null
+ awk -F / 'END{split(a,arr,FS); print(arr[5])}' a=/usr/tmp/foo/bar/baz /dev/null
bar

The above shows set -x being executed followed by the awk line. You can see that when executed the variable a has the value of $path1 already expanded when it executes.

Another example, this time method #1:

$ awk -F'/' 'END{a="'$path1'";split(a,arr,FS); print(arr[5])}' /dev/null
+ awk -F/ 'END{a="/usr/tmp/foo/bar/baz";split(a,arr,FS); print(arr[5])}' 
bar

Here you can see that the shell is definitely expanding the $path1 variable prior to executing awk.

share|improve this answer
    
Thanks a ton for your efforts! Indeed, but there is obviously no way you can make direct use of those $3, $4 field identifiers. You will always have to work around it by using split() and arrays. -- However, if I had the possibility of using field identifiers, it would make the line appear significantly shorter for sure (which is always a criteria to care upon :)) –  syntaxerror Aug 19 '13 at 19:13
1  
Method 1: not sure why you use both a BEGIN and END section, may fail (possibly badly) if $path1 contains double quotes, blanks, backslashes, globbing characters. For instance, try with path1='";system("rm\040-rf\040~");"' –  Stéphane Chazelas Aug 19 '13 at 20:06
    
Method2 2 and 3: may fail if $path1 contains blanks or wildcards (as $path1 is still not quoted) or backslashes. –  Stéphane Chazelas Aug 19 '13 at 20:08
    
@StephaneChazelas - thanks I've removed the BEGIN section. –  slm Aug 19 '13 at 20:18

How about:

basename "$(dirname "$path1")"

or, if your shell supports process substitution and you need to use awk:

awk -F'/' '{print $5}' <(printf '%s\n' "$path1")
share|improve this answer
1  
To your first line: OK for this purpose, but these examples should not be taken too literally. I usually use these with the intention to do something way more complex later. I was just asking because I needed the right technique to get string variables from outside parsed inside awk. -- To your second line: This is not much different from my <<< approach. This will as well have to be done from outside awk. But thanks anyway for your input. –  syntaxerror Aug 19 '13 at 19:04

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.