solaris echo " [ " character problem - bash

i have bash script such as
for i in `echo a [ matched.lines`
do
echo $i
done
why output of this script below
a
[
matched.lines
i want output text as it is
a [ matched.lines
how can i do that
thanks for help

The script echos each token seperately. Use echo -n $i inside do ... done.

i have replaced ' ' characters to '' with sed global replace and there is no new line on output of script thanks for help

to read a file using shell, use while read loop
while read -r loop
do
case "$line" in
*]* ) echo $line;;
esac
done <"file"

Sorry but that's not going to do what you want it to. Your for loop is using a space as a delimiter to it's arguments. This will cause each one of the item's to echo on a seperate line. Adding a -n doesn't work here because you will not get the \n at the end of the line. Honestly I don't see what it is your trying to do here. If you just want to echo this "a [ matched.lines" as a string you can do this:
for i in "a [ matched.lines";do
echo $i
done
But I feel you're going to misunderstand how to use a for loop....

Related

Bash Script Not Concatenating String as Intended [duplicate]

I have a code like that
var="before"
echo "$someString" | sed '$someRegex' | while read line
do
if [ $condition ]; then
var="after"
echo "$var" #first echo
fi
done
echo "$var" #second echo
Here first echo print "after", but second is "before". How can I make second echo print "after". I think it is because of pipe buy I don't know how figure out.
Thanks for any solutions...
answer edit:
I corrected it and it works fine. Thanks eugene for your useful answer
var="before"
while read line
do
if [ $condition ]; then
var="after"
echo "$var" #first echo
fi
done < <(echo "$someString" | sed '$someRegex')
echo "$var" #second echo
The reason for this behaviour is that a while loop runs in a subshell when it's part of a pipeline. For the while loop above, a new subshell with its own copy of the variable var is created.
See this article for possible workarounds: I set variables in a loop that's in a pipeline. Why do they disappear after the loop terminates? Or, why can't I pipe data to read?.

How to get first character of variable

I'm trying to get the first character of a variable, but I'm getting a Bad substitution error. Can anyone help me fix it?
code is:
while IFS=$'\n' read line
do
if [ ! ${line:0:1} == "#"] # Error on this line
then
eval echo "$line"
eval createSymlink $line
fi
done < /some/file.txt
Am I doing something wrong or is there a better way of doing this?
-- EDIT --
As requested - here's some sample input which is stored in /some/file.txt
$MOZ_HOME/mobile/android/chrome/content/browser.js
$MOZ_HOME/mobile/android/locales/en-US/chrome/browser.properties
$MOZ_HOME/mobile/android/components/ContentPermissionPrompt.js
To get the first character of a variable you need to say:
v="hello"
$ echo "${v:0:1}"
h
However, your code has a syntax error:
[ ! ${line:0:1} == "#"]
# ^-- missing space
So this can do the trick:
$ a="123456"
$ [ ! "${a:0:1}" == "#" ] && echo "doesnt start with #"
doesnt start with #
$ a="#123456"
$ [ ! "${a:0:1}" == "#" ] && echo "doesnt start with #"
$
Also it can be done like this:
$ a="#123456"
$ [ "$(expr substr $a 1 1)" != "#" ] && echo "does not start with #"
$
$ a="123456"
$ [ "$(expr substr $a 1 1)" != "#" ] && echo "does not start with #"
does not start with #
Update
Based on your update, this works to me:
while IFS=$'\n' read line
do
echo $line
if [ ! "${line:0:1}" == "#" ] # Error on this line
then
eval echo "$line"
eval createSymlink $line
fi
done < file
Adding the missing space (as suggested in fedorqui's answer ;) ) works for me.
An alternative method/syntax
Here's what I would do in Bash if I want to check the first character of a string
if [[ $line != "#"* ]]
On the right hand side of ==, the quoted part is treated literally whereas * is a wildcard for any sequence of character.
For more information, see the last part of Conditional Constructs of Bash reference manual:
When the ‘==’ and ‘!=’ operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below in Pattern Matching
Checking that you're using the right shell
If you are getting errors such as "Bad substitution error" and "[[: not found" (see comment) even though your syntax is fine (and works fine for others), it might indicate that you are using the wrong shell (i.e. not Bash).
So to make sure you are using Bash to run the script, either
make the script executable and use an appropriate shebang e.g. #!/bin/bash
or execute it via bash my_script
Also note that sh is not necessarily bash, sometimes it can be dash (e.g. in Ubuntu) or just plain ol' Bourne shell.
Try this:
while IFS=$'\n' read line
do
if ! [ "${line:0:1}" = "#" ]; then
eval echo "$line"
eval createSymlink $line
fi
done < /some/file.txt
or you can use the following for your if syntax:
if [[ ! ${line:0:1} == "#" ]]; then
TIMTOWTDI ^^
while IFS='' read -r line
do
case "${line}" in
"#"*) echo "${line}"
;;
*) createSymlink ${line}
;;
esac
done < /some/file.txt
Note: I dropped the eval, which could be needed in some (rare!) cases (and are dangerous usually).
Note2: I added a "safer" IFS & read (-r, raw) but you can revert to your own if it is better suited. Note that it still reads line by line.
Note3: I took the habit of using always ${var} instead of $var ... works for me (easy to find out vars in complex text, and easy to see where they begin and end at all times) but not necessary here.
Note4: you can also change the test to : *"#"*) if some of the (comments?) lines can have spaces or tabs before the '#' (and none of the symlink lines does contain a '#')

Parsing .csv file in bash, not reading final line

I'm trying to parse a csv file I made with Google Spreadsheet. It's very simple for testing purposes, and is basically:
1,2
3,4
5,6
The problem is that the csv doesn't end in a newline character so when I cat the file in BASH, I get
MacBook-Pro:Desktop kkSlider$ cat test.csv
1,2
3,4
5,6MacBook-Pro:Desktop kkSlider$
I just want to read line by line in a BASH script using a while loop that every guide suggests, and my script looks like this:
while IFS=',' read -r last first
do
echo "$last $first"
done < test.csv
The output is:
MacBook-Pro:Desktop kkSlider$ ./test.sh
1 2
3 4
Any ideas on how I could have it read that last line and echo it?
Thanks in advance.
You can force the input to your loop to end with a newline thus:
#!/bin/bash
(cat test.csv ; echo) | while IFS=',' read -r last first
do
echo "$last $first"
done
Unfortunately, this may result in an empty line at the end of your output if the input already has a newline at the end. You can fix that with a little addition:
!/bin/bash
(cat test.csv ; echo) | while IFS=',' read -r last first
do
if [[ $last != "" ]] ; then
echo "$last $first"
fi
done
Another method relies on the fact that the values are being placed into the variables by the read but they're just not being output because of the while statement:
#!/bin/bash
while IFS=',' read -r last first
do
echo "$last $first"
done <test.csv
if [[ $last != "" ]] ; then
echo "$last $first"
fi
That one works without creating another subshell to modify the input to the while statement.
Of course, I'm assuming here that you want to do more inside the loop that just output the values with a space rather than a comma. If that's all you wanted to do, there are other tools better suited than a bash read loop, such as:
tr "," " " <test.csv
cat file |sed -e '${/^$/!s/$/\n/;}'| while IFS=',' read -r last first; do echo "$last $first"; done
If the last (unterminated) line needs to be processed differently from the rest, #paxdiablo's version with the extra if statement is the way to go; but if it's going to be handled like all the others, it's cleaner to process it in the main loop.
You can roll the "if there was an unterminated last line" into the main loop condition like this:
while IFS=',' read -r last first || [ -n "$last" ]
do
echo "$last $first"
done < test.csv

Overwrite last line on terminal

My bash-script looks as following:
echo "Description:"
while [ $finishInput -eq 0 ]; do
read tmp
desc="$desc"$'\n'"$tmp"
if [ -z "$tmp" ]; then
finishInput="1"
fi
done
echo -n "Maintainer:"
read maintainer
It reads to the desc var until a empty line is passed. After that, i want to read in other stuff.
When executing my current script it looks like this:
Description:
Line 1
Line 2
Maintainer:
I would like to overwrite the last empty line with the "Maintainer:".
I searched for a solution but only found suggestions which were like
echo -n "Old line"
echo -e "\r new line"
which stays on the line and overwrites it. This is not possible in my case.
In your example you delete the text at the same line. When you want to return to the previous line use \e[1A, and to clear that line, use \e[K:
echo 'Old line'
echo -e '\e[1A\e[Knew line'
When you want to go N lines up, use \e[<N>A
Found a great guide on escape sequences and wanted to expand on some of the discussions here.
When you write out to a terminal, you move an invisible cursor around, much like you do when you write in any text editor. When using echo, it will automatically end the output with a new line character which moves the cursor to the next line.
$ echo "Hello" && echo " World"
Hello
World
You can use -n to prevent the new line and if you echo again after this, it will append it to the end of that line
$ echo -n "Hello" && echo " World"
Hello World
The cursor remains where it was so, on it's own, we can't use -n to overwrite the previous line, we need to move the cursor to the left. To do that we need to give it an escape sequence, which we let echo know we're going to use with -e and then move the cursor by providing a return carriage \r which puts the cursor at the beginning of the line.
$ echo -n "Hello" && echo -e "\rWorld"
World
That may look like it worked, but see what happens with
$ echo -n "A longer sentance" && echo -e "\rShort sentance"
Short sentancence
See the extra characters? Simply writing over the line only changes the characters where we wrote them.
To fix this, the accepted answer above uses the escape character \e[0K to erase everything after the cursor, after the cursor has moved left. i.e. \r move to beginning \e[0K erase to end.
$ echo -n "A longer sentance" && echo -e "\r\e[0KShort sentance"
Short sentance
Important \e to begin escape sequences works in zsh but not in sh and not necessarily in bash, however \033 works in all of them. If you want your script to work anywhere, you should preference \033
$ echo -n "A longer sentance" && echo -e "\r\033[0KShort sentance"
Short sentance
But escape characters can provide even more utility. For example \033[1A moves the cursor to the previous line so we don't need the -n on the previous echo:
$ echo "A longer sentance" && echo -e "\r\033[1A\033[0KShort sentance"
Short sentance
\r move to the beginning \033[1A move up \033[0K erase to the end
Finally, this is all a bit messy in my book, so you can turn this into a function:
overwrite() { echo -e "\r\033[1A\033[0K$#"; }
Using $# just puts all the parameters of the function into the string
$ echo Longer sentance && overwrite Short sentence
Short sentence
I built a function from Dennis Williamsons Comment:
function clearLastLine() {
tput cuu 1 && tput el
}
Thanks to Dennis Williamson
If you echo without the newline character echo -n "Something", you can use \r with your next echo to move the 'cursor' to the beginning of the line echo -e "\\rOverwrite something".
#!/bin/bash
CHECK_MARK="\033[0;32m\xE2\x9C\x94\033[0m"
echo -e "\n\e[4mDoing Things\e[0m"
echo -n "doing thing 1..."
sleep 1
echo -e "\\r${CHECK_MARK} thing 1 done"
Just be aware that if your new string is shorter that your old string, the tail of your old string will still be visible. Note the done.. in the gif above.
If you want to run a script in a loop and not blow up your scrollback, you can use the following pattern:
while sleep 10s; do
echo -n $(script)
echo -n -e "\e[0K\r"
done
Just replace the script command with your own.
#!/bin/bash
echo "Description:"
while test -z $finishInput; do
read -s tmp
desc="$desc"$'\n'"$tmp"
if [ -z "$tmp" ]; then
finishInput=1
else
echo $tmp
fi
#echo "fi="$finishInput;
done
echo -n "Maintainer:"
read maintainer
This solution avoids the empty line, but input is not echoed before the lines are complete.
Hint: My version of bash did not accept "[ $finishInput -eq 0 ]".

evaluating lines from stdout

I have a bash script that is executing a program in a loop. I want to evaluate each line from the stdout and do something if it matches my condition.
I still want to be able to see stdout on the screen. Is there a simple way to accomplish this? Thanks!
There are several variants of looping over input, but one possibility is thus:
my_cmd | while read line; do
echo "$line"
my_process "$line"
done
This should do what you want:
for string in "a" "b" "c"
do
output=`echo ${string}`
echo ${output}
if [ ${output} == "b" ] ; then
echo "do something"
fi
done
Just replace the first echo with your program.

Resources