How do I specify the position of a width parameter? - linux

Apologies if this has been posted before. I've been searching all over and can't find an answer.
According to man printf, "FORMAT controls the output as in C printf." It refers to printf(3).
According to man 3 printf, you can specify a variable width in a given position. It says:
Instead of a decimal digit string one may write … "*m$" (for some decimal integer m) to specify that the precision is given in the m-th argument … which must be of type int.
This is the part where I'm struggling. To give a simple example, suppose I wish to print a string with width 14.
$ printf '[%14s]\n' Something
[ Something]
I can use a variable instead:
$ WIDTH=14
$ printf '[%*s]\n' ${WIDTH} Something
[ Something]
The tricky part comes when I want to tell printf that the width argument is in a different position. To keep things simple for this example, I'll leave it where it is, in position 1. Following the instructions, I write the following.
Built-in version:
$ printf '[%*1$s]\n' ${WIDTH} Something
bash: printf: `1': invalid format character
Version in /usr/bin:
$ /usr/bin/printf '[%*1$s]\n' ${WIDTH} Something
[/usr/bin/printf: %*1: invalid conversion specification
Even using the example from the manual gives an error.
Built-in version:
$ printf '%2$*1$d' 6 34
bash: printf: `$': invalid format character
Version in /usr/bin:
$ /usr/bin/printf '%2$*1$d' 6 34
/usr/bin/printf: %2$: invalid conversion specification
As you can see, I get an error every time. I have struggled to see what I'm doing wrong, and I simply cannot find any example online.
How should this be formatted, please, or is the manual just wrong?
Lubuntu 18.04
GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu)
builtin printf: unknown version
/usr/bin/printf printf (GNU coreutils) 8.28
Thank you

No, the extension that comes from Single UNIX Specification to C printf function to specify the position of positional parameter with $ is not supported in bash printf and I doubt it's supported in any of shells printf implementations. I believe the reason is that it requires additional work from the developers, while it's very rarely used extension.
is the manual just wrong?
FORMAT controls the output as in C printf.
You are reading the documentation for GNU version of printf, taken from the amazing Linux man-pages project. I would say C printf is documented in the C standard.

If your shell's printf doesn't support parameter indexing, you can still call Perl to the rescue!
$ perl -we 'printf q([%1$*2$d]), #ARGV' 36 6
[ 36]
See printf and, more importantly, sprintf.

Related

How to resolve arithmetic syntax error in printf

I am running below code in SunOS as .sh file
#!/usr/bin/bash
#lerning linux scripting
#
printf "%-5s %-10s %-4s\n" No Name Mark
printf "%-5s %-10s %-4.2f\n" 1 James 80.1234
printf "%-5s %-10s %-4.2f\n" 2 Sarah 99.8923
But I am getting below error when run above one in SunOS
No Name Mark
: arithmetic syntax error.sh[5]: printf: 80.1234
: arithmetic syntax error0.1234
linux_sc.sh[5]: printf: warning: invalid argument of type f
80.12
: arithmetic syntax error.sh[6]: printf: 99.8923
: arithmetic syntax error9.8923
linux_sc.sh[6]: printf: warning: invalid argument of type f
99.89
So yeah, pretty sure you're using a script that has Windows style CRLF line endings, while Unix systems use LF. This happens when you write the file on Windows and don't transfer it to the other computer in a way that translates line endings.
Taking your sample script and running it through unix2dos and then trying to run it gives:
No Name Mark
: invalid numberprintf: 80.1234
1 James 0.00
: invalid numberprintf: 99.8923
2 Sarah 0.00
Not exactly the same output, but I'm testing on Linux, not SunOS, so that's to be expected. Close enough, though, to suggest it's the issue.
If you have dos2unix installed on the SunOS box, run your script through it and see if that fixes the problem. Or perl -pi -e 's/\r//' your.sh is another way of stripping the extraneous carriage returns. Or tr -d '\r' < your.sh > new.sh or any number of other ways; just depends on what a typical SunOS setup has installed. I haven't used it in years; don't remember off the top of my head.

Why does space in Bash string subscripts matter here?

I'm experimenting with bash scripting and noticed the following behavior:
file="test.jar"
echo "${file: -4}" #prints .jar
echo "${file:-4}" #prints test.jar
Very confusing behavior actually. Can someone explain why the second case prints the whole test.jar?
This is due to inconsistent syntax. {"string":-} means default (all the string), whatever follows -. So you need either a space or parenthesis:
{"string": -4}
{"string":(-4)}
Read bash string manipulation.
This is a compromise due to the timeline of features being added to Bash.
The ${parameter:-word} or ${parameter-word} syntax for "replace parameter with word if parameter is null or unset (:-) / null (-)" was around for pretty much always; the - version was already in the Version 7 Bourne Shell.
The ${parameter:offset:length} and ${parameter:offset} syntax for "substring of parameter starting at offset (with optional length length)" was introduced in Bash 2.0 (no conflict so far).
Negative offsets and length specifications for the substring construct were introduced in Bash 4.2. This leads to a problem:
$ string=01234567890abcdefgh
$ echo ${string:7:-2} # Not ambiguous
7890abcdef
$ echo ${string:-7} # Interpreted as "undefined/null, or..."
01234567890abcdefgh
$ echo ${string: -7} # Interpreted as offset from the end
bcdefgh
$ echo ${string:(-7)} # Interpreted as offset from the end
bcdefgh
The space before - or the parentheses around the negative offset are there to tell the expansion apart from the :- (default value) expansion.
If the space is not there, the expansion ${file:-4} is interpreted as "print 4 if the parameter file is null or unset, and the expansion of file otherwise".
References:
BashFAQ/061: Is there a list of which features were added to specific releases (versions) of Bash?
Bash hackers wiki: Bash changes
Shell parameter expansion in the bash manual
Bash NEWS file describing feature added per version

What does "cat -A" command option mean in Unix

I'm currently working on a Unix box and came across this post which I found helpful, in order to learn about cat command in Unix. At the bottom of the page found this line saying: -A = Equivalent to -vET
As I'm new into Unix, I'm unaware of what does this mean actually? For example lets say I've created a file called new using cat and then apply this command to the file:
cat -A new, I tried this command but an error message comes up saying it's and illegal option.
To cut short, wanted to know what does cat -A really mean and how does it effect when I apply it to a file. Any help would be appreciated.
It means show ALL.
Basically its a combination of -vET
E : It will display '$' at the end of every line.
T : It will display tab character as ^I
v : It will use ^ and M-notation
^ and M-notation:
(Display control characters except for LFD(LineFeed or NewLine) and TAB using '^' notation and precede characters that have the high bit set with
'M-') M- notation is a way to display high-bit characters as low bit ones by preceding them with M-
You should read about little-endian and big-endian if you like to know more about M notation.
For example:
!http://i.imgur.com/0DGET5k.png?1
Check your manual page as below and it will list all options avaialable with your command and check is there -A present, if it is not present it is an illegal option.
man cat
It displays non-printing characters
In Mac OS you need to use -e flag and
-e Display non-printing characters (see the -v option), and display a dollar sign (`$') at the end of each line.

Bash arrays and negative subscripts, yes or no?

The GNU bash manual tells me
An indexed array is created automatically if any variable is assigned
to using the syntax
name[subscript]=value
The subscript is treated as an arithmetic expression that must
evaluate to a number. If subscript evaluates to a number less than
zero, it is used as an offset from one greater than the array’s
maximum index (so a subcript of -1 refers to the last element of the
array).
So I figure I will give it a try and get the following result:
$ muh=(1 4 'a' 'bleh' 2)
$ echo $muh
1
$ echo ${muh[*]}
1 4 a bleh 2 # so far so good so now I'll try a negative ...
$ echo ${muh[-1]}
-bash: muh: bad array subscript # didn't go as planned!
Did I do something wrong, or is the website wrong, or is gnu bash that different from the bash I am running under CentOS? Thanks!
If you just want the last element
$ echo ${muh[*]: -1}
2
If you want next to last element
$ echo ${muh[*]: -2:1}
bleh
According to Greg Wooledge's wiki, (which links to the bash changelog) the negative index syntax was added to bash in version 4.2 alpha.
Bash beore 4.2 (like the default one on Macs these days) doesn't support negative subscripts. Apart from the "substring expansion" used in the accepted answer, a possibly cleaner workaround is to count the desired index from the array start within the brackets:
$ array=(one two three)
$ echo "${array[${#array[#]}-1]}"
three
With this approach, you can pack other parameter expansion operations into the term, e.g. "remove matching prefix pattern" th:
$ echo "${array[${#array[#]}-1]#th}"
ree
If you do man bash the section on arrays does not list this behavior. It might be something new (gnu?) in bash.
Fails for me in CentOS 6.3 (bash 4.1.2)
The negative subscript works perfectly fine for me on my computer with Ubuntu 14.04 / GNU bash version 4.3.11(1) however it returns:
line 46: [-1]: bad array subscript
When I try to run the same script on 4.2.46(1). I

Can colon be used as identifier?

I saw a code in The Bash command :(){ :|:& };: will spawn processes to kernel death. Can you explain the syntax? as follows
user#host$ :(){ :|:& };:
Here colon used as identifier for function name.
Can colon be used as identifier?
Yes, it can.
$ :()
> {
> echo "hello from : :)"
> }
$ :
hello from : :)
According to the documentation:
name
A word consisting solely of letters, numbers, and underscores, and beginning with a letter or underscore. Names are used as shell variable and function names. Also referred to as an identifier.
No, the colon is not valid in function names. So either the bomb doesn't work in bash, or the documentation is failing.
I shortly thought that the colon might refer to the built-in operator, but I don't see how that could get the expected semantics.
The implementation seems to be inconsistent. You can define and call a function containing colons and Google even suggests this for packages in their style guide.
Though I noticed you can not export nor unset these functions.
#!/usr/bin/env bash
foo:bar() {
echo "foo:bar called"
}
foo:bar
export -f foo:bar
unset foo:bar
The export won't complain but if you call another bash script afterwards foo:bar is not available.
The unset will even trigger an error:
/foo/bar: line 11: unset: `foo:bar': not a valid identifier
$ bash --version
GNU bash, version 4.2.46(1)-release (x86_64-redhat-linux-gnu)

Resources