How to reassign a variable (zsh) when using source utility - macos

The code below tests if the character from a string is matching regex or not.
str=")Y"
c="${str:0:1}"
if [[ $c =~ [A-Za-z0-9_] ]]; then
echo "YES"
output=$c
else
echo "NO"
output="-"
fi
echo $output
I am running it with
source script-name.sh
However, instead of expected printout
NO
-
I am getting an empty line without dash
NO
I understand the issue is somehow around the way i (re-)assign output variable which being me to questions
How to do it properly?
Why source utility has such implication?
UPD_1: it is for Mac's zsh, not bash
UPD_2: the issue occurs only when running script via 'source' utility like "source script-name.sh"
Running with "./script-name.sh" yield correct result as well.

Your problem can be simplified to do on the zsh command line a
echo -
which also doesn't output anything. Similarily, a
echo - x
would output simply x and not - x.
This does not depend on whether or not you are on the Mac. If you would do a
echo - -
or a
=echo -
(the latter using the external program echo) would print a dash.
Therefore, you can change in your script-name.sh a
=echo $output
or a
echo - $output
and you should be fine.
The zshbuiltins man-page explains it, when describing the echo command:
the first dash, possibly following options, is not printed, but everything following it is printed as an argument.
Therefore, in zsh, at least when printing a variable, it is better to also use a lone dash for the safe side.

Your code gives the expected output for bash 4.2.46 on RHEL7.
Are you maybe using zsh?
See echo 'the character - (dash) in the unix command line
EDIT: Ok, if it's zsh, you probably have to use a hack:
if [[ ${output} == '-' ]]; then
echo - ${output}
else
echo ${output}
fi
or use printf:
printf $output"\n"

Related

How to write the content of a unknown shell variable to stdout in a safe way

I only know what I have read so far, and I am confused about how to actually echo a variable as is.
echo "$var" might fail if var='-n'
printf '%s\n' "$var" might fail because of shell not implementig printf
echo -- "$var" might fail because it is a gnu extension
So if i would have to guess:
echo x"$var"|sed 's#^x##1' would be the only way, but I have never encountered that pattern. Why?
As a concrete question:
for source; do
target="$(echo "$source"|sed 's#[^a-z0-9]\+#.#')"
# do stuff with $source and $target
done
Does this work, or could someone "hack" / "break" my script by putting a file named '-n' somewhere, assuming my script is executed by some my_script * cron?
How do I write echo "$var" so it does not break?
Does this work, or could someone "hack" / "break" my script by putting
a file named '-n' somewhere?
There is nothing wrong with:
target="$(echo "$source"|sed 's#[^a-z0-9]\+#.#')"
What is happening:
"$(...)" is a command substitution which will substitute the results of the command within as the value -- in which case the result is assigned to target.
echo "$source"|sed 's#[^a-z0-9]\+#.#' simply pipes the output of echo (e.g. what is in source) to sed for the simple substitution of every character not lowercase or a digit followed by + with a period 1. Note: the quotes ".." around $source ARE proper within the command substitution.
There is no inherent reason assigning -n to a variable will cause any mischief. What you do with the variable is another question, but suffice it to say it is hard to see any problem.
"POSIX-shell's out there not implementing printf" -- Huh? Any shell not implementing printf would be more an exception rather than the rule. See printf - The Open Group Library that is POSIX.
If you are attempting to printf output that begins with '-' simply precede the output with "--" to indicate End-of-Options before the string your want to print and things will go fine. With your example of "-n", printf is about the only way you will output a variable beginning with the single '-', for example:
$ t="-n"
$ printf -- "%s\n" "$t"
-n
(note: you don't have to include "--" in printf "%s\n" "$var", the only time you must include it is with printf -- "-foo\n" or you will receive an "invalid option error".
For echo you can enable interpretation of backslash escapes with -e and include a backspace, e.g.
$ echo -e " \b$t"
-n
I think that has covered all issues. If not, let me know. Also, if you have any additional questions, drop a comment below or edit and add to your question.
footnotes:
note: + isn't part of basic regular expressions and it need not be escaped, but if there is any question, it is safer to include in a character class of its own, e.g. [^a-z0-9][+].

printing output of command history 1 from shell script

Here's my problem, from console if I type the below,
var=`history 1`
echo $var
I get the desired output. But when I do the same inside a shell script, it is not showing any output. Also, for other commands like pwd, ls etc, the script shows the desired output without any issue.
As value of variable contains space, add quotes around it.
E.g.:
var='history 1'
echo $var
I believe all you need is this as follows:
1- Ask user for the number till which user need to print the history in script.
2- Run the script and take Input from user and get the output as follows:
cat get_history.ksh
echo "Enter the line number of history which you want to get.."
read number
if [[ $# -eq 0 ]]
then
echo "Usage of script: get_history.ksh number_of_lines"
exit
else
history "$number"
fi
Added logic where it will check arguments if number of arguments passed is 0 then it will exit from script then.
By default history is turned off in a script, therefore you need to turn it on:
set -o history
var=$(history 1)
echo "$var"
Note the preferred use of $( ) rather than the deprecated backticks.
However, this will only look at the history of the current process, that is this shell script, so it is fairly useless.

shell script that determines file type

Having trouble with this problem, i could use some help ....
create a shell script, fileType.sh, that takes a single command line parameter, a file path (might be relative or absolute). The script should examine that file and print a single line consisting of the phrase
Windows ASCII
if the files is an ASCII text file with CR/LF line terminators, or
Something else
if the file is binary or ASCII with “Unix” LF line terminators.
For example:
./fileType.sh ~cs252/Assignments/ftpAsst/d3.dat
Windows ASCII
./fileType.sh /bin/cat
Something else
./fileType.sh fileType.sh
Something else
./fileType.sh /usr/share/dict/words
Something else
Your script should not produce any other output when given a legal path to an existing file. (It may produce anything you like if given an incorrect path to a non-existent file.)
New to Unix, I have some C++ experience.
This is what i have in my script.
#!/bin/sh
grep -r 'Windows ASCII' $1
if $(file == "ASCII TEXT");then
echo Windows ASCII
else
echo Something Else
fi
this is my output:
./fileType.sh: 4: ./fileType.sh: [file: not found
Something Else
#!/bin/sh
grep -r "Windows ASCII" $1
if test "$( file "$1")" = ' ASCII text, with CRFL line terminators'; then
echo Windows ASCII
else
echo Something else
fi
my output now is just Something else when the file I am passing in should give an output of Windows ASCII ...
Your script is trying to execute a command named "[file". That is not what you want. Offering no advice about the advisability of this solution, but the syntax to implement what you seem to want is:
if test "$( file "$1" )" = 'ASCII TEXT'; then
or
if [ "$( file "$1" )" = 'ASCII TEXT' ]; then
[ is a command (whether builtin to the shell or external is irrelevant), which is exactly the same as the command test except that it expects its last argument to be ]. It is a heinous kludge that was an attempt to make the syntax appear as if the brackets are tokens in the shell language. IMO, it is far better to use test as it makes it clear that it is not part of the grammar.
The actual implementation requires that you interpret/test the result of the file return a bit more. William's answer pointed you in that direction. It is a bit unclear whether you are needing a solutions for traditional shell programming (as in Bourne Shell) or whether you are looking for a Bash (or equivalent) solution. Above, I guess the confusion surrounds the use of:
if test "$( file "$1" )" = 'ASCII TEXT'; then
While this will confirm for you that the file is text with lf separators, exclusive of all others, it does not give you a way to tell whether the file contains lines with CRLF line-ends or not (just that it is not 'ASCII TEXT'). To make the exclusive determination whether the file contains CRLF line-ends, you need to test the file output to find CRLF.
Regardless of the shell, the approach is the same. Capture the result of the file command, and test whether the result contains CRLF. The difference between shells is how you accomplish the test. The following examples provide a way to do the testing the Bourne Shell and the same again in Bash (note: there are many ways to accomplish the test in either, this is just one approach in each):
#!/bin/sh
line=`file -b "$1"`
## Bourne Shell
win=0
for i in $line; do # search for CRLF in $line
if test `expr match "$i" "CRLF"` -gt 0 ; then
win=1
break
fi
done
if test "$win" -eq 1 ; then
printf "Windows ASCII\n"
else
printf "Something else\n"
fi
## BASH
if [[ "$line" =~ "CRLF" ]]; then # use substring operator
printf "Windows ASCII\n"
else
printf "Something else\n"
fi
Example with/CRLF:
$ ./Type.sh ../../utl/tst/config.win.ini
Windows ASCII
Windows ASCII
Example with/LF:
$ ./Type.sh ../../utl/tst/config.ini
Something else
Something else

Echo newline in Bash prints literal \n

How do I print a newline? This merely prints \n:
$ echo -e "Hello,\nWorld!"
Hello,\nWorld!
Use printf instead:
printf "hello\nworld\n"
printf behaves more consistently across different environments than echo.
Make sure you are in Bash.
$ echo $0
bash
All these four ways work for me:
echo -e "Hello\nworld"
echo -e 'Hello\nworld'
echo Hello$'\n'world
echo Hello ; echo world
echo $'hello\nworld'
prints
hello
world
$'' strings use ANSI C Quoting:
Words of the form $'string' are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard.
You could always do echo "".
For example,
echo "Hello,"
echo ""
echo "World!"
On the off chance that someone finds themselves beating their head against the wall trying to figure out why a coworker's script won't print newlines, look out for this:
#!/bin/bash
function GET_RECORDS()
{
echo -e "starting\n the process";
}
echo $(GET_RECORDS);
As in the above, the actual running of the method may itself be wrapped in an echo which supersedes any echos that may be in the method itself. Obviously, I watered this down for brevity. It was not so easy to spot!
You can then inform your comrades that a better way to execute functions would be like so:
#!/bin/bash
function GET_RECORDS()
{
echo -e "starting\n the process";
}
GET_RECORDS;
Simply type
echo
to get a new line
POSIX 7 on echo
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html
-e is not defined and backslashes are implementation defined:
If the first operand is -n, or if any of the operands contain a <backslash> character, the results are implementation-defined.
unless you have an optional XSI extension.
So I recommend that you should use printf instead, which is well specified:
format operand shall be used as the format string described in XBD File Format Notation [...]
the File Format Notation:
\n <newline> Move the printing position to the start of the next line.
Also keep in mind that Ubuntu 15.10 and most distros implement echo both as:
a Bash built-in: help echo
a standalone executable: which echo
which can lead to some confusion.
str='hello\nworld'
$ echo | sed "i$str"
hello
world
You can also do:
echo "hello
world"
This works both inside a script and from the command line.
On the command line, press Shift+Enter to do the line break inside the string.
This works for me on my macOS and my Ubuntu 18.04 (Bionic Beaver) system.
For only the question asked (not special characters etc) changing only double quotes to single quotes.
echo -e 'Hello,\nWorld!'
Results in:
Hello,
World!
There is a new parameter expansion added in Bash 4.4 that interprets escape sequences:
${parameter#operator} - E operator
The expansion is a string that is the value of parameter with
backslash escape sequences expanded as with the $'…' quoting
mechanism.
$ foo='hello\nworld'
$ echo "${foo#E}"
hello
world
I just use echo without any arguments:
echo "Hello"
echo
echo "World"
To print a new line with echo, use:
echo
or
echo -e '\n'
This could better be done as
x="\n"
echo -ne $x
-e option will interpret backslahes for the escape sequence
-n option will remove the trailing newline in the output
PS: the command echo has an effect of always including a trailing newline in the output so -n is required to turn that thing off (and make it less confusing)
My script:
echo "WARNINGS: $warningsFound WARNINGS FOUND:\n$warningStrings
Output:
WARNING : 2 WARNINGS FOUND:\nWarning, found the following local orphaned signature file:
On my Bash script I was getting mad as you until I've just tried:
echo "WARNING : $warningsFound WARNINGS FOUND:
$warningStrings"
Just hit Enter where you want to insert that jump. The output now is:
WARNING : 2 WARNINGS FOUND:
Warning, found the following local orphaned signature file:
If you're writing scripts and will be echoing newlines as part of other messages several times, a nice cross-platform solution is to put a literal newline in a variable like so:
newline='
'
echo "first line${newline}second line"
echo "Error: example error message n${newline}${usage}" >&2 #requires usage to be defined
If the previous answers don't work, and there is a need to get a return value from their function:
function foo()
{
local v="Dimi";
local s="";
.....
s+="Some message here $v $1\n"
.....
echo $s
}
r=$(foo "my message");
echo -e $r;
Only this trick worked on a Linux system I was working on with this Bash version:
GNU bash, version 2.2.25(1)-release (x86_64-redhat-linux-gnu)
You could also use echo with braces,
$ (echo hello; echo world)
hello
world
This got me there....
outstuff=RESOURCE_GROUP=[$RESOURCE_GROUP]\\nAKS_CLUSTER_NAME=[$AKS_CLUSTER_NAME]\\nREGION_NAME=[$REGION_NAME]\\nVERSION=[$VERSION]\\nSUBNET-ID=[$SUBNET_ID]
printf $outstuff
Yields:
RESOURCE_GROUP=[akswork-rg]
AKS_CLUSTER_NAME=[aksworkshop-804]
REGION_NAME=[eastus]
VERSION=[1.16.7]
SUBNET-ID=[/subscriptions/{subidhere}/resourceGroups/makeakswork-rg/providers/Microsoft.Network/virtualNetworks/aks-vnet/subnets/aks-subnet]
Sometimes you can pass multiple strings separated by a space and it will be interpreted as \n.
For example when using a shell script for multi-line notifcations:
#!/bin/bash
notify-send 'notification success' 'another line' 'time now '`date +"%s"`
With jq:
$ jq -nr '"Hello,\nWorld"'
Hello,
World
Additional solution:
In cases, you have to echo a multiline of the long contents (such as code/ configurations)
For example:
A Bash script to generate codes/ configurations
echo -e,
printf might have some limitation
You can use some special char as a placeholder as a line break (such as ~) and replace it after the file was created using tr:
echo ${content} | tr '~' '\n' > $targetFile
It needs to invoke another program (tr) which should be fine, IMO.

shell script working fine on one server but not on another

the following script is working fine on one server but on the other it gives an error
#!/bin/bash
processLine(){
line="$#" # get the complete first line which is the complete script path
name_of_file=$(basename "$line" ".php") # seperate from the path the name of file excluding extension
ps aux | grep -v grep | grep -q "$line" || ( nohup php -f "$line" > /var/log/iphorex/$name_of_file.log & )
}
FILE=""
if [ "$1" == "" ]; then
FILE="/var/www/iphorex/live/infi_script.txt"
else
FILE="$1"
# make sure file exist and readable
if [ ! -f $FILE ]; then
echo "$FILE : does not exists. Script will terminate now."
exit 1
elif [ ! -r $FILE ]; then
echo "$FILE: can not be read. Script will terminate now."
exit 2
fi
fi
# read $FILE using the file descriptors
# $ifs is a shell variable. Varies from version to version. known as internal file seperator.
# Set loop separator to end of line
BACKUPIFS=$IFS
#use a temp. variable such that $ifs can be restored later.
IFS=$(echo -en "\n")
exec 3<&0
exec 0<"$FILE"
while read -r line
do
# use $line variable to process line in processLine() function
processLine $line
done
exec 0<&3
# restore $IFS which was used to determine what the field separators are
IFS=$BAKCUPIFS
exit 0
i am just trying to read a file containing path of various scripts and then checking whether those scripts are already running and if not running them. The file /var/www/iphorex/live/infi_script.txt is definitely present. I get the following error on my amazon server-
[: 24: unexpected operator
infinity.sh: 32: cannot open : No such file
Thanks for your helps in advance.
You should just initialize file with
FILE=${1:-/var/www/iphorex/live/infi_script.txt}
and then skip the existence check. If the file
does not exist or is not readable, the exec 0< will
fail with a reasonable error message (there's no point
in you trying to guess what the error message will be,
just let the shell report the error.)
I think the problem is that the shell on the failing server
does not like "==" in the equality test. (Many implementations
of test only accept one '=', but I thought even older bash
had a builtin that accepted two '==' so I might be way off base.)
I would simply eliminate your lines from FILE="" down to
the end of the existence check and replace them with the
assignment above, letting the shell's standard default
mechanism work for you.
Note that if you do eliminate the existence check, you'll want
to either add
set -e
near the top of the script, or add a check on the exec:
exec 0<"$FILE" || exit 1
so that the script does not continue if the file is not usable.
For bash (and ksh and others), you want [[ "$x" == "$y" ]] with double brackets. That uses the built-in expression handling. A single bracket calls out to the test executable which is probably barfing on the ==.
Also, you can use [[ -z "$x" ]] to test for zero-length strings, instead of comparing to the empty string. See "CONDITIONAL EXPRESSIONS" in your bash manual.

Resources