Printing two variables - only second one printed when run from script - string

I'm trying to print to sting1 and string2 with a space in between using shell script.
defined strings a,b and printing them using echo with a space in between. However, a is replaced by b as below.
a='30 Jan 2016 22:'
b='30 Jan 2016 23:'
echo $a $b
Output:
30 Jan 2016 23:
String1 is override by string2 to display. However, the same is working from command prompt as below:
$ a='30 Jan 2016 22:'
$ b='30 Jan 2016 23:'
$ echo $a $b
30 Jan 2016 22: 30 Jan 2016 23:
Why do these behaviors differ?

The most likely cause of this is DOS newlines in your text files. This means you have a CRLF at the end of each line, sending the cursor back to the beginning.
When interpreted by UNIX tools, for which a LF (\n) is the only character involved in a newline, the CR (\r) preceding it becomes part of the data.
That makes the actual behavior of your code this:
a='30 Jan 2016 22:'$'\r'
b='30 Jan 2016 23:'$'\r'
echo $a $b
...first printing $a, then returning the cursor to the beginning of the line and overwriting it with $b.

Related

Add blank line before a certain phrase in a text file in Linux?

I'm using Kali Linux, trying to sort out some input from Nmap. Basically, I ran a scan from NMap, and need to extract specific pieces of information from it. I've got it to show everything I need using the following command:
cat discovery.txt | grep 'Nmap scan report for\|Service Info: OS:\|OS CPE:\|OS guesses:\|OS matches\|OS details'
Essentially, each section of information I need will start with "Nmap scan report for [IP ADDRESS]"
I'd like to add to my command to have it create a blank line before every appearance of the word "Nmap", to clearly separate each chunk of information.
Is there any command I can use to do this?
sed '/Nmap/i
' file
That's a literal newline after the i
A demo: add a newline before each line ending with a "0" or a "5"
seq 19 | sed '/0$\|5$/i
'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Sure, you can use Perl.
perl -pe 's/^Nmap/\nNmap/'

Want to assign the result of the command echo ^`date` to a variable. but when i am trying to do i am not getting the excepted result

echo ^`date`
^Wed Jan 21 05:49:37 CST 2015
deeps=`echo ^`date``
echo $deeps
Actual Result:
^date
Expected Result :
^Wed Jan 21 05:49:37 CST 2015
Need help on this
Try this method
deeps=^$(date)
echo $deeps
Output :
^Wed Jan 21 18:44:25 IST 2015
Backticks are horribly outdated and should not be used any more -- using $() instead will save you many headaches
Use a backtick, or use Command substitution. Like
# Shell command substitution.
echo ^$(date)
or
# backticks.
deeps=`date`
echo ^$deeps
Both output (the requested)
^Wed Jan 21 08:16:01 EST 2015
Simply because neither of the other (correct) answers actually explain the problem I'm adding another answer.
tl;dr Compare the backtick version to the $() version
The difference between
echo ^`date`
and
deeps=`echo ^`date``
is how many backticks are on the line and how the shell is parsing the line.
echo ^`date`
has a single pair of backticks and the shell parses it as (where the [] are marking "parts" of the line)
[echo] [^][`date`]
The
`date`
bit is then expanded via Command Substitution and so the line becomes
[echo] [^Wed Jan 21 05:49:37 CST 2015]
and then echo spits out the desired ^Wed Jan 21 05:49:37 CST 2015.
This line however
deeps=`echo ^`date``
is parsed as
[deeps][=][`echo ^`][date][``]
which you can already see is quite different and not correct (this happens because backticks cannot be nested for the record).
There are now two command substitutions on this line echo ^ and the empty string so the line becomes
[deeps][=][^][date][]
or with the "words" combined
[deeps][=][^date]
which then assigns ^date to deeps and echo $deeps then gets you ^date.
The $() form of command substitution, no the other hand, does nest and thus
deeps=$(echo ^$(date))
parses as
[deeps][=][$([echo] [^][$([date])])]
which properly runs both date and echo on the result. Though, as indicated in the other answers, the wrapping echo is not necessary as deeps=^$(date) will work just fine by itself.

where my '[]' is?, bash program

in linux ,bash program.
I write this:
msg=`date '+%m-%d %H:%M'`" alipay recharge [$sum] in past 15 mins"
echo $msg >> $MonitorLog
Mostly it works ,but sometime.the result will like this:
07-15 09:01 card recharge 0 in past 30 mins
My sentence changes. not 0, if $sum=0 ,it should be:
07-15 09:01 card recharge [0] in past 30 mins
I don't know where my '[]' is? can you help me ,thanks a lot.
You are hitting shell globbing. See the output below.
$ ls -l
total 4
-rw-r--r-- 1 root root 0 Jul 14 21:40 5
$ sum=10
$ msg=`date '+%m-%d %H:%M'`" alipay recharge [$sum] in past 15 mins"
$ echo $msg
07-14 21:41 alipay recharge [10] in past 15 mins
$ sum=5
$ msg=`date '+%m-%d %H:%M'`" alipay recharge [$sum] in past 15 mins"
$ echo $msg
07-14 21:41 alipay recharge 5 in past 15 mins
$ echo "$msg"
07-14 21:41 alipay recharge [5] in past 15 mins
#Etan Reisinger's answer contains the crucial pointer:
Shell expansions are inadvertently applied to $msg, because it is unquoted.
tl;dr:
Double-quote your variable references to protect them from interpretation by the shell:
echo "$msg" >> "$MonitorLog" # due to double-quoting, contents of $msg used as is
Generally, the only reason NOT to double-quote a variable reference is the express intent to have the shell interpret the value (apply expansions to it) - see below.
In the case at hand, here's what happens if you do not double-quote $msg:
After splitting the value of $msg into words by whitespace (word splitting), pathname expansion is applied to each:
I.e., each word that looks like a glob (a filename pattern), is matched against filenames - in the specified directory or, without a path component, in the current one - and if matches are found, that word is replaced by matching filenames.
A word such as [0] happens to be a valid glob ([...] encloses a set of matching characters; in this case, the set is made up of only 1 char., 0), and if a file named 0 happens to be present in the current directory, [0] is replaced by that matching filename, 0 - effectively making the [] disappear - this is what happened in the OP's case.
(See man bash, section Pathname Expansion, for what constitutes valid globs.)

Applying a patch to files with spaces in names

Here's an output of diff -u "temp temp/docs 1.txt" "temp temp/docs 2.txt":
--- temp temp/docs 1.txt Mon Apr 7 16:15:08 2014
+++ temp temp/docs 2.txt Mon Apr 7 16:18:45 2014
## -2,6 +2,6 ##
22
333
4444
-555555
+55555
666666
7777777
However, feeding this diff to patch -u fails with following message:
can't find file to patch at input line 3
Perhaps you should have used the -p or --strip option?
The text leading up to this was:
--------------------------
|--- temp temp/docs 1.txt Mon Apr 7 16:15:08 2014
|+++ temp temp/docs 2.txt Mon Apr 7 16:18:45 2014
--------------------------
Apparently, the spaces are the problem; is there a way to make patch to work on files with spaces in names?
No, GNU patch doesn't support this. Here's the official statement: http://www.gnu.org/software/diffutils/manual/html_node/Unusual-File-Names.html#Unusual%20File%20Names
Gnu patch 2.6.1 (linux) seems to obey at least 1 space (not tried with more) if the filename is separated from the date with tab.
YYMV
I encountered the same problem when trying to establish conventions how to do manual version control with diff and patch.
I found out that GNU "diff" creates quoted path names in the patch headers if they contain spaces, while BusyBox "diff" doesn't.
Neither GNU nor BusyBox "patch" accepts quoted path names.
If the problem is just embedded spaces within filenames, it can therefore be avoided by using "busybox patch" rather than GNU "patch".
Another solution is to postprocess the output of GNU "diff" before feeding it into "patch":
sed 's,^\([-+]\{3\} \)"\([^"]*\)",\1\2,' $PATCHFILE | patch -p1
This works whether $PATCHFILE was created with GNU or busybox diff, but will only work with unified diff format.
Unfortunately, it turns out that leading or trailing spaces in filenames cannot be preserved with this method, as "patch" will skip them when parsing the path names from the patch instructions.
The approach will neither work if the filename starts with a literal double quote - but then, who uses such file names?
Most of the time, however, the above approach works just fine.
Finally a note of other approaches I have also tried but which did not work:
First I tried to replace the quotation of the whole path names by individually quoted path name components. This failed because "patch" does not use double quotes as meta-characters at all. It considers them to be normal literal characters.
Then I tried to replace all spaces by "\040" like CVS does - but "patch" does not seem to accept octal-escapes either, and this failed too.

Extracting IP addresses from text file with batch

I have a text file with data like this:
Aug 21 [10.23.5.5] Teardown dynamic
Aug 18 [10.150.1.45] Aug 21 15:28:34 otoldc
Aug 24 [10.96.5.10] Aug 21 2012 18:58:26 HYD
Aug 24 [10.96.5.10] Aug 22 2012 18:58:26 HYD
Aug 21 [192.168.15.231] sendmail[18831]
I need to remove everything except IP addresses surrounded by "[" and "]". String length before "[" is fixed. String length after "]" varied.
I tried use one of existing solutions here but couldn't get success. Is it possible to do it using batch?
Thanks:-)
directly from command line: for /f "tokens=2 delims=[]" %F in (file.txt) do echo %F. Redirect as you wish.
Not as flexible as sed/awk & regexes, but it does not require external tools.
If you plan to put together something more complex though, I would really look to more powerful tools - apart from already mentioned awk or Perl natural choice on Win would be Powershell.
Install a version of sed if it's not already on your system.
$ sed -r -e 's/^[^[]*\[([^]\]*)].*/\1/' file.txt
10.23.5.5
10.150.1.45
10.96.5.10
10.96.5.10
192.168.15.231
This sed one-liner 'script' outputs each input line after removing everything from the lines except the contents inside the first set of [] square brackets on the line - it does not check those contents to make sure it matches an IP address.
You tagged this as batch, so I assume this is on Windows and not linux. All the same, I'd highly recommend you head over to Cygwin's website and download a copy. This will give you access to the cat and grep commands, which make this much simpler. Once you have Cygwin installed, you can run the following command to parse out the IP addresses from your log file.
cat your.log | grep -oE '([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}' > ips.txt
Cheers

Resources