shell glob pattern trouble - unix

I'm trying to print out an out the corresponds to an input. The input would be a number and the output would print out the number in english. I am having trouble with the syntax/regular expressions in unix. If I were to run this with an input of 277, there would only be "one-hundred" as output. Where it should be "one-hundred twenty." Is there something wrong with it..? I've looked everywhere and it seems right.
I know it's not complete yet but it is bothering me. Any tips?
case "$1" in
1[0-9][0-9]) # 100's
echo one-hundred
if [ "$1" == 12[0-9] ]
then
echo twenty
fi
;;

The regex syntax is =~ not == so your if needs to be [[ "$1" =~ 12[0-9] ]] though not every shell supports that syntax

Related

Zsh returning `<function>:<linenumber> = not found`

I used the have the following tmux shortcut function defined in a separate script and aliased, which worked fine but was messy. I decided to move it to my .zshrc where it naturally belongs, and encountered a problem I wasn't able to figure out.
function t () {re='^[0-9]+$'
if [ "$1" == "kill" ]
then
tmux kill-session -t $2
elif [[ "$1" =~ "$re" ]]
then
tmux attach-session -d -t $1
fi}
I source my .zshrc, call the function, and get:
t:1: = not found
I know the function is defined:
╭─bennett#Io [~] using
╰─○ which t
t () {
re='^[0-9]+$'
if [ "$1" == "kill" ]
then
tmux kill-session -t $2
elif [[ "$1" =~ "$re" ]]
then
tmux attach-session -d -t $1
fi
}
I'm assuming this is complaining about the first line of the function. I've tried shifting the first line of the function down several lines, which doesn't change anything except which line the error message refers to. Any clue what's going on? I haven't found anything relating to this specific issue on SO.
The command [ (or test) only supports a single = to check for equality of two strings. Using == will result in a "= not found" error message. (See man 1 test)
zsh has the [ builtin mainly for compatibility reasons. It tries to implement POSIX where possible, with all the quirks this may bring (See the Zsh Manual).
Unless you need a script to be POSIX compliant (e.g. for compatibility with other shells), I would strongly suggest to use conditional expressions, that is [[ ... ]], instead of [ ... ]. It has more features, does not require quotes or other workarounds for possibly empty values and even allows to use arithmetic expressions.
Wrapping the first conditional in a second set of square-brackets seemed to resolve the issue.
More information on single vs double brackets here:
Is [[ ]] preferable over [ ] in bash scripts?

Getting highest extensions value in unix script

I need to create new files with extensions like: file.1, file.2, file.3 and then check if files with certain numbers exist and create file.(n+1) where n is number of highest, existing file. I was trying to get extensions using basename but it doesn't want to get couple of files
file=`basename $file.*`
ext=${file##*.}
It only works when I input whole file name like $file.3
If the filenames are guaranteed not to have newline characters in them, you can, for example, use standard unix text processing tools:
printf '%s\n' file.* | #full list
sed 's/.*\.//' | #extensions
grep '^[0-9][0-9]*$' | #numerical extensions
awk '{ if($0>m) m=$0} END{ print m }' #get maximum
Here's my take on this.
You can do this entirely in standard awk.
$ awk '{ext=FILENAME;sub(/.*\./,"",ext)} ext>n&&ext~/^[0-9]+$/{n=ext}{nextfile} END {print n}' *.*
Broken out for easier reading:
$ awk '
{
# Capture the extension...
ext=FILENAME
sub(/.*\./,"",ext)
}
# Then, if we have a numeric extension that is bigger than "n"...
ext > n && ext ~ /^[0-9]+$/ {
# let "n" be that extension.
n=ext
}
{
# We aren't actually interested in the contents of this file, so move on.
nextfile
}
# No more files? Print our result.
END {print n}
' *.*
The idea here is that we'll step through the list of filenames and let awk do ALL the processing to capture and "sort" the extensions. (We're not really sorting, we're just recording the highest number as we pass through the files.)
There are a few provisos with this solution:
This only works if all the files have a non-zero length. Technically awk conditions are being compared on "lines of the file", so if there are no lines, awk will pass right by that file.
You don't really need to use the ext variable, you can modify FILENAME directly. I included it for improved readability.
The nextfile command is fairly standard, but not universal. If you have a very old machine, or are running an esoteric variety of unix, nextfile may not be included. (I don't expect this to be a problem.)
Another alternative, which might be easier for you, would be to implement the same logic directly in POSIX shell:
$ n=0; for f in *.*; do ext=${f##*.}; if expr "$ext" : '[0-9][0-9]*$' >/dev/null && [ "$ext" -gt "$n" ]; then n="$ext"; fi; done; echo "$n"
Or, again broken out for easier reading (or scripting):
n=0
for f in *.*; do
ext=${f##*.}
if expr "$ext" : '[0-9][0-9]*$' >/dev/null && [ "$ext" -gt "$n" ]; then
n="$ext"
fi
done
echo "$n"
This steps through all files using a for loop, captures the extension, makes sure it's numeric, determines whether it's greater than "n" and records if it it is, then prints its result.
It requires no pipes and no external tools except expr, which is a POSIX.1 tool available on every system.
One proviso for this solution is that if you have NO files with extensions (i.e. *.* returns no files), this script will erroneously report that the highest numbered extension is 0. You can of course handle that easily enough, but I thought I should mention it.
Thanks for all answers, I've came up with quite similar and a bit simpler idea which I'd like to present it:
for i in file.*; do
#reading the extensions
ext=${i##*.}
if [ "$ext" -gt "$n" ];
then
#increasing n
n=$((n+1))
fi
done
then if we want to get number exceeding n by one
until [[ $a -gt "$n" ]]; do
a=$((a+1))
done
and finally a is one number bigger then number of file extensions. So if there are three files: file.1 file.2 file.3 the returned value will be 4.

Capturing and testing output command in ZSH

I have tried countless ways to get what I want, but nothing seems to work. I always end up with something like 2:not found.
I want to capture the output of a command, and then test if it equals "!", like so:
function test() {
local testv=$(command) 2>/dev/null
if [ $(#testv) == "!" ]; then
echo "Exclamation mark!"
else
echo "No exclamation mark."
fi
}
How should I rewrite the code above to avoid the error test:2: = not found?
This should work:
if [ $testv = '!' ]; then
There were several problems here:
$(...) runs a command and substitutes its output; you want to substitute a variable value, so use $var or ${var}.
I have no idea what the # was doing there. ${#var} will get the length of $var, but that's not what you want here.
The test command (which [ is a synonym for) doesn't understand ==, so use = (if you're a C programmer that'll look wrong, but this is shell not C).
I don't think this is a problem in a script, but for interactive input "!" doesn't do what you expect. I used '!' to make sure the exclamation mark couldn't be interpreted as a history option.
Alternately, you could use [[ ]] instead of [ ], since it understands == (and has somewhat cleaner syntax in general):
if [[ $testv == '!' ]]; then
BTW, I'm trusting from the tag that this script is running in zsh; if not, the syntax will be a bit different (basic shells don't have [[ ]], and anything other than zsh will do unwanted parsing on the value of $testv unless it's in double-quotes). If you're not sure (or want it to be portable), here's a version that should work in any posix-compliant shell:
if [ "$testv" = '!' ]; then
Try with this:
local testv=$(command 2>/dev/null)
since it's the output of the command you want to redirect.
(I have no idea what you mean by $(#testv) though.)

UNIX command line argument referencing issues

I'm trying to tell unix to print out the command line arguments passed to a Bourne Shell script, but it's not working. I get the value of x at the echo statement, and not the command line argument at the desired location.
This is what I want:
./run a b c d
a
b
c
d
this is what I get:
1
2
3
4
What's going on? I know that UNIX is confused as per what I'm referencing in the shell script (the variable x or the command line argument at the x'th position". How can I clarify what I mean?
#!/bin/sh
x=1
until [ $x -gt $# ]
do
echo $x
x=`expr $x + 1`
done
EDIT: Thank you all for the responses, but now I have another question; what if you wanted to start counting not at the first argument, but at the second, or third? So, what would I do to tell UNIX to process elements starting at the second position, and ignore the first?
echo $*
$x is not the xth argument. It's the variable x, and expr $x+1 is like x++ in other languages.
The simplest change to your script to make it do what you asked is this:
#!/bin/sh
x=1
until [ $x -gt $# ]
do
eval "echo \${$x}"
x=`expr $x + 1`
done
HOWEVER (and this is a big however), using eval (especially on user input) is a huge security problem. A better way is to use shift and the first positional argument variable like this:
#!/bin/sh
while [ $# -gt 0 ]; do
x=$1
shift
echo ${x}
done
If you want to start counting a the 2nd argument
for i in ${#:2}
do
echo $i
done
A solution not using shift:
#!/bin/sh
for arg in "$#"; do
printf "%s " "$arg"
done
echo

How to compare two files in shell script?

Here is my scenario.
I have two files which are having records with each record's 3-25 characters is an identifier. Based on this I need to compare both of them and update the old file with the new file data if their identifiers match. Identifiers start with 01.
Please look at the script below.
This is giving some error as "argument expected at line 12 which I am not able to understand.
#!/bin/ksh
while read line
do
c=`echo $line|grep '^01' `
if [ $c -ne NULL ];
then
var=`echo $line|cut -c 3-25`
fi
while read i
do
d=`echo $i|grep '^01' `
if [ $d -ne NULL ];
then
var1=`echo $i|cut -c 3-25`
if [ $var -eq $var1 ];
then
$line=$i
fi
fi
done < test_monday
done < test_sunday
Please help me out thanks in advance
I think what you need is :
if [ "$d" != NULL ];
Try.
I think you could use the DIFF command
diff file1 file2 > whats_the_diff.txt
Unless you are writing a script for portability to the original Bourne shell or others that do not support the feature, in Bash and ksh you should use the [[ form of test for strings and files.
There is a reduced need for quoting and escaping, additional conditions such as pattern and regular expression matching and the ability to use && and || instead of -a and -o.
if [[ $var == $var1 ]]
Also, "NULL" is not a special value in Bash and ksh and so your test will always succeed since $d is tested against the literal string "NULL".
if [[ $d != "" ]]
or
if [[ $d ]]
For numeric values (not including leading zeros unless you're using octal), you can use numeric expressions. You can omit the dollar sign for variables in this context.
numval=41
if ((++numval >= 42)) # increment then test
then
echo "don't panic"
fi
It's not necessary to use echo and cut for substrings. In Bash and ksh you can do:
var=${line:3:23}
Note: cut uses character positions for the beginning and end of a range, while this shell construct uses starting position and character count so you have to adjust the numbers accordingly.
And it's a good idea to get away from using backticks. Use $() instead. This can be nested and quoting and escaping is reduced or easier.

Resources