UNIX command line argument referencing issues - unix

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

Related

UNIX: How to print contents of variable with formatting.

I just want to print ls -l the same way it looks from the command line (each file on a new line). I have looked every where for a solution and know my solution should work but for some reason it doesn't. I have tried:
#!/bin/csh
set list = `ls -l`
echo "$list"
and:
#!/bin/csh
set list = "`ls -l`"
echo "$list"
with no luck. What I really want to do is use grep on ls -l later (so maby I'm going about this wrong), but I can't because it prints list as one long line.
(and yes, I have to use csh)
I don't know how to get around csh's behaviour of joining words together when you echo, but you may be able to use array-like functionality and a loop. For example:
#!/bin/csh
set list=( "`printf 'a\nb\nc\n'`" )
echo "count=$#list"
echo "2 = $list[2]"
echo
set n=0
while ( $n < $#list )
# n += 1
echo "$n : $list[$n]"
end
Which for me produces the output:
count=3
2 = b
1 : a
2 : b
3 : c
Note that I'm using tcsh on FreeBSD. Your csh may be different, you haven't mentioned your platform.
To bring this back to your list of files question, you can replicate the output you're looking for with a similar loop:
#!/bin/csh
set list=( "`ls -l`" )
set n=0
while ( $n < $#list )
# n += 1
echo "$list[$n]"
end
The important consideration here is that within (command substitution) backquotes (`...`), output is word-separated by whitespace, whereas inside double quotes ("..."), output is word-separated by newlines.
That said...
What I really want to do is use grep on ls -l later (so maby I'm going about this wrong), but I can't because it prints list as one long line.
Entirely possible! :-) But without a full understanding of the underlying problem you're trying to solve, helping you achieve your solution is the best we can do. Beware the dreaded XY Problem.

Unix create and use variables inside expect script

In my attempts to automatize access to a remote computer,
I am trying to create and use variables inside an expect script.
I am trying to do the following:
#!/bin/csh -f
/user/bin/expect<<EOF_EXPECT
set USER [lindex $USER 0]
set HOST [lindex $HOST 0]
set PASSWD [lindex $PASSWD 0]
set timeout 1
spawn ssh $USER#$HOST
expect "assword:"
send "$PASSWRD\r"
expect ">"
set list_ids (`ps -ef | grep gedit | awk '{ print $2 }'`)
expect ">"
for id in ($list_ids)
send "echo $id\r"
end
send "exit\r"
EOF_EXPECT
Several challenges with this code:
The ps | grep | awk line does not act as in the shell. It does not extract only the pid using the awk command. Instead, it takes the whole line.
The variable $list_ids is unrecognized although I set it using what I thought is variable setting inside expect script.
Lastly, how to do the for loop so that $id and $id_list will be recognized?
I am using csh. $env(list_ids) does not work for me, $env is undefined.
Both shell and tcl variables are marked with $. The contents of your here document are being expanded by your shell. You don't want that. csh doesn't have a value for $2 so expands it to the empty string and the awk command ends up becoming ps -ef | grep gedit | awk '{ print }'. Which is why you get the entire lines in the output.
You have your contexts confused here a bit. You need to escape the $ from the external csh if you want it to make it through to the embedded awk command. (Which is horrible but apparently the case for csh.)
In general you need to not try to merge csh and tcl commands/etc. like this it will greatly help you understand what is happening.
What do you mean "unrecognized"? Are you getting any other errors (like from the set command)?
I think you are looking for foreach:
$ tclsh
% foreach a [list 1 2 3 4] b [list 5 6 7 8] c [list a b c d] d [list w x y z]
puts "$a $b $c $d"
}
1 5 a w
2 6 b x
3 7 c y
4 8 d z
%
$env(list_ids) is a tcl variable. That csh doesn't know anything about it is unrelated to anything (well other than the problem in point one above so escape it). If you export list_ids in the csh session that runs the tcl script then $env(list_ids) should work in the expect script.
You don't want the () around the value in the set command either I don't think. They are literal there I believe. If you are trying to create a tcl list there from the (shell expanded) output from that ps pipeline then you need:
set list_ids [list `ps ....`]
But as I said before you don't really want to be mixing contexts like that.
If you can use a non-csh shell that would likely help here also as csh is just generally not good at all.
Also, not embedding an expect script inside a csh script would help if you can just write an expect script as the script file directly.
Reading here helped me a lot:
http://antirez.com/articoli/tclmisunderstood.html
The following lines do the trick, and answer all questions:
set list_ids [list {`ps -ef | grep gedit | awk '{print \$2 }'}]
set i 0
while {[lindex \$list_ids \$i] > 0} {
puts [lindex \$list_ids \$i]
set i [expr \$i + 1]
}

Assigning a value to a variable in a loop and printing it using korn shell

I am having problem when trying to assign a value to a variable in a loop and trying to print it using korn shell. I want to use that variable in later part of my script. So I am trying to test with in the loop by printing the value of the dynamic varible I just assigned to it from my array.
#!/usr/bin/ksh
clear
BINPATH=/usr/bin
SVR_LIST=servers_list
set -A SERVERS `cat $SVR_LIST`
typeset -i i=0
while [ $i -lt ${#SERVERS[#]} ] ; do
#print ${SERVERS[$i]}
typeset -l s${i}=${SERVERS[$i]}
echo "Value of Variable is " ${s{$i}}
(( i=i+1 ))
done
I am getting following error.
./test.sh[12]: ${s{$i}}: bad substitution
Try it with
eval echo "Value of Variable is \${s$i}"
You need two evaluation steps (once for $i and once for the rest), hence you need eval.

ZSH subString extraction

Goal
In ZSH script, for a given args, I want to obtain the first string and the rest.
For instance, when the script is named test
sh test hello
supposed to extract h and ello.
ZSH manual
http://zsh.sourceforge.net/Doc/zsh_a4.pdf
says:
Subscripting may also be performed on non-array values, in which case the subscripts specify a
substring to be extracted. For example, if FOO is set to ‘foobar’, then ‘echo $FOO[2,5]’ prints
‘ooba’.
Q1
So, I wrote a shell script in a file named test
echo $1
echo $1[1,1]
terminal:
$ sh test hello
hello
hello[1,1]
the result fails. What's wrong with the code?
Q2
Also I don't know how to extract subString from n to the last. Perhaps do I have to use Array split by regex?
EDIT: Q3
This may be another question, so if it's proper to start new Thread, I will do so.
Thanks to #skishore Here is the further code
#! /bin/zsh
echo $1
ARG_FIRST=`echo $1 | cut -c1`
ARG_REST=`echo $1 | cut -c2-`
echo ARG_FIRST=$ARG_FIRST
echo ARG_REST=$ARG_REST
if $ARG_FIRST = ""; then
echo nullArgs
else
if $ARG_FIRST = "#"; then
echo #Args
else
echo regularArgs
fi
fi
I'm not sure how to compare string valuables to string, but for a given args hello
result:
command not found: h
What's wrong with the code?
EDIT2:
What I've found right
#! /bin/zsh
echo $1
ARG_FIRST=`echo $1 | cut -c1`
ARG_REST=`echo $1 | cut -c2-`
echo ARG_FIRST=$ARG_FIRST
echo ARG_REST=$ARG_REST
if [ $ARG_FIRST ]; then
if [ $ARG_FIRST = "#" ]; then
echo #Args
else
echo regularArgs
fi
else
echo nullArgs
fi
EDIT3:
As the result of whole, this is what I've done with this question.
https://github.com/kenokabe/GitSnapShot
GitSnapShot is a ZSH thin wrapper for Git commands for easier and simpler usage
A1
As others have said, you need to wrap it in curly braces. Also, use a command interpreter (#!...), mark the file as executable, and call it directly.
#!/bin/zsh
echo $1
echo ${1[1,1]}
A2
The easiest way to extract a substring from a parameter (zsh calls variables parameters) is to use parameter expansion. Using the square brackets tells zsh to treat the scalar (i.e. string) parameter as an array. For a single character, this makes sense. For the rest of the string, you can use the simpler ${parameter:start:length} notation instead. If you omit the :length part (as we will here), then it will give you the rest of the scalar.
File test:
#!/bin/zsh
echo ${1[1]}
echo ${1:1}
Terminal:
$ ./test Hello
H
ello
A3
As others have said, you need (preferably double) square brackets to test. Also, to test if a string is NULL use -z, and to test if it is not NULL use -n. You can just put a string in double brackets ([[ ... ]]), but it is preferable to make your intentions clear with -n.
if [[ -z "${ARG_FIRST}" ]]; then
...
fi
Also remove the space between #! and /bin/zsh.
And if you are checking for equality, use ==; if you are assigning a value, use =.
RE:EDIT2:
Declare all parameters to set the scope. If you do not, you may clobber or use a parameter inherited from the shell, which may cause unexpected behavior. Google's shell style guide is a good resource for stuff like this.
Use builtins over external commands.
Avoid backticks. Use $(...) instead.
Use single quotes when quoting a literal string. This prevents pattern matching.
Make use of elif or case to avoid nested ifs. case will be easier to read in your example here, but elif will probably be better for your actual code.
Using case:
#!/bin/zsh
typeset ARG_FIRST="${1[1]}"
typeset ARG_REST="${1:1}"
echo $1
echo 'ARG_FIRST='"${ARG_FIRST}"
echo 'ARG_REST='"${ARG_REST}"
case "${ARG_FIRST}" in
('') echo 'nullArgs' ;;
('#') echo '#Args' ;;
(*)
# Recommended formatting example with more than 1 sloc
echo 'regularArgs'
;;
esac
using elif:
#!/bin/zsh
typeset ARG_FIRST="${1[1]}"
typeset ARG_REST="${1:1}"
echo $1
echo 'ARG_FIRST='"${ARG_FIRST}"
echo 'ARG_REST='"${ARG_REST}"
if [[ -z "${ARG_FIRST}" ]]; then
echo nullArgs
elif [[ '#' == "${ARG_FIRST}" ]]; then
echo #Args
else
echo regularArgs
fi
RE:EDIT3
Use "$#" unless you really know what you are doing. Explanation.
You can use the cut command:
echo $1 | cut -c1
echo $1 | cut -c2-
Use $() to assign these values to variables:
ARG_FIRST=$(echo $1 | cut -c1)
ARG_REST=$(echo $1 | cut -c2-)
echo ARG_FIRST=$ARG_FIRST
echo ARG_REST=$ARG_REST
You can also replace $() with backticks, but the former is recommended and the latter is somewhat deprecated due to nesting issues.
So, I wrote a shell script in a file named test
$ sh test hello
This isn't a zsh script: you're calling it with sh, which is (almost certainly) bash. If you've got the shebang (#!/bin/zsh), you can make it executable (chmod +x <script>) and run it: ./script. Alternatively, you can run it with zsh <script>.
the result fails. What's wrong with the code?
You can wrap in braces:
echo ${1} # This'll work with or without the braces.
echo ${1[3,5]} # This works in the braces.
echo $1[3,5] # This doesn't work.
Running this: ./test-script hello gives:
./test-script.zsh hello
hello
llo
./test-script.zsh:5: no matches found: hello[3,5]
Also I don't know how to extract subString from n to the last. Perhaps do I have to use Array split by regex?
Use the [n,last] notation, but wrap in braces. We can determine how long our variable is with, then use the length:
# Store the length of $1 in LENGTH.
LENGTH=${#1}
echo ${1[2,${LENGTH}]} # Display from `2` to `LENGTH`.
This'll produce ello (prints from the 2nd to the last character of hello).
Script to play with:
#!/usr/local/bin/zsh
echo ${1} # Print the input
echo ${1[3,5]} # Print from 3rd->5th characters of input
LENGTH=${#1}
echo ${1[2,${LENGTH}]} # Print from 2nd -> last characters of input.
You can use the cut command:
But that would be using extra baggage - zsh is quite capable of doing all this on it's own without spawning multiple sub-shells for simplistic operations.

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