in bash, with
$ echo {1..10}
1 2 3 4 5 6 7 8 9 10
I can get a numbers sequence, but in some case I need
01 02 03 ... 10
how I can get this ?
and how I can get ?
001 002 ... 010 011 .. 100
This will work in any shell on a machine that has coreutils installed (thanks commenters for correcting me):
seq -w 1 10
and
seq -w 1 100
Explanation:
the option -w will:
Equalize the widths of all numbers by padding with zeros as necessary.
seq [-w] [-f format] [-s string] [-t string] [first [incr]] last
prints a sequence of numbers, one per line (default), from
first (default 1), to near last as possible, in increments of incr (default
1). When first is larger than last the default incr is -1
use seq command with -f parameter, try:
seq -f "%02g" 0 10
results:
00
01
02
03
04
05
06
07
08
09
10
seq -f "%03g" 0 10
results:
000
001
002
003
004
005
006
007
008
009
010
printf "%02d " {1..10} ; echo
Output:
01 02 03 04 05 06 07 08 09 10
Similarly:
printf "%03d " {1..100} ; echo
In more recent versions of bash, simply:
echo {01..10}
And:
echo {001..100}
for i in {01..99}; do
echo $i
done
will return :
01
02
03
04
05
06
07
08
09
10
...
Replacing 01 with 001 and 99 with 999 or 100 will do what you expect also.
$ printf "%02d " {0..10}; echo
00 01 02 03 04 05 06 07 08 09 10
$ printf "%03d " {0..100}; echo
000 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100
Just vary the field width in the format string (2 and 3 in this case) and of course the brace expansion range. The echo is there just for cosmetic purposes, since the format string does not contain a newline itself.
printf is a shell builtin, but you likely also have a version from coreutils installed, which can be used in-place.
awk only:
awk 'BEGIN { for (i=0; i<10; i++) printf("%02d ", i) }'
The following will work in bash
echo {01..10}
**EDIT seeing the answers around me I just wanted to add this, in the case we're talking about commands that will work under any terminal
yes | head -n 100 | awk '{printf( "%03d ", NR )}' ##for 001...100
or
yes | head -n 10 | awk '{printf( "%03d ", NR )}' ##for 01..10
echo 0{0..9}
You can get: 00 01 02 03 04 05 06 07 08 09
echo 0{0..9} 1{0..9}
You can get: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19
echo 00{0..9} 0{10..99}
You can get 001 .. 099
There are so many ways to do this! My personal favorite is:
yes | grep y | sed 100q | awk '{printf( "%03d ", NR )}'; echo
Clearly, neither the sed nor the grep are necessary (the grep being far more trivial, since if you omit the sed you need to change the awk), but they contribute to the overall satisfaction of the solution! The final echo is not really necessary either, but it's always nice to have a trailing newline.
Another nice option is:
yes | nl -ba | tr ' ' 0 | sed 100q | cut -b 4-6
Or (less absurdly):
yes '' | sed ${top-100}q | nl -ba -w ${width-3} -n rz
as commented by favoretti, seq is your friend.
But there is a caveat:
seq -w uses the second argument to set the format it will use.
Thus, the command seq -w 1 9 will print the sequence 1 2 3 4 5 6 7 8 9
To print the sequence 01 .. 09 you need to do the following:
seq -w 1 09
Or for clarities sake use the same format on both ends, for instance:
seq -w 000 010 for the series 001 002 003 ... 010
And you can also use a step argument that also works in reverse:
seq -w 10 -1 01' for 10,09,08...01 orseq -w 01 2 10` for 01,03,05,07,09
Related
Grep doesn't seem to match certain strings from man output. It seems to be random in that I can't work out any rhyme or reason as to whether a string will match or not.
man sed | head -7:
SED(1) BSD General Commands Manual SED(1)
NAME
sed -- stream editor
SYNOPSIS
$ man sed | head -7 | grep sed # no match
$ man sed | head -7 | grep stream # match on "stream"
sed -- stream editor
$ man sed | head -7 | grep '\-\-' # match on "--"
sed -- stream editor
$ man sed | head -7 | grep NAME # no match
$ man sed | head -7 | grep SYNOPSIS # no match
This also happens when redirecting the output to a file and grepping that
$ man sed | head -7 > /tmp/sed.man
$ cat /tmp/sed.man | grep sed # no match
$ cat /tmp/sed.man | grep stream # match on "stream"
sed -- stream editor
$ grep sed /tmp/sed.man # no match
$ grep stream /tmp/sed.man # match on "stream"
sed -- stream editor
grep: grep (BSD grep) 2.5.1-FreeBSD
man: version 1.6c
macOS: 10.14.6 Beta
bash: GNU bash, version 5.0.7(1)-release (x86_64-apple-darwin18.5.0)
$ man sed | head -7 | hexdump -C
00000000 0a 53 45 44 28 31 29 20 20 20 20 20 20 20 20 20 |.SED(1) |
00000010 20 20 20 20 20 20 20 20 20 20 20 42 53 44 20 47 | BSD G|
00000020 65 6e 65 72 61 6c 20 43 6f 6d 6d 61 6e 64 73 20 |eneral Commands |
00000030 4d 61 6e 75 61 6c 20 20 20 20 20 20 20 20 20 20 |Manual |
00000040 20 20 20 20 20 20 20 20 20 53 45 44 28 31 29 0a | SED(1).|
00000050 0a 4e 08 4e 41 08 41 4d 08 4d 45 08 45 0a 20 20 |.N.NA.AM.ME.E. |
00000060 20 20 20 73 08 73 65 08 65 64 08 64 20 2d 2d 20 | s.se.ed.d -- |
00000070 73 74 72 65 61 6d 20 65 64 69 74 6f 72 0a 0a 53 |stream editor..S|
00000080 08 53 59 08 59 4e 08 4e 4f 08 4f 50 08 50 53 08 |.SY.YN.NO.OP.PS.|
00000090 53 49 08 49 53 08 53 0a |SI.IS.S.|
00000098
Googling is hard for this problem as any combination of "man" or "grep" doesn't mention my problem that strings (with no special characters) are not matching.
man-pages are using the roff-format (https://man.openbsd.org/roff). Do the following:
man sed > sed.man
vi sed.man
so you see:
SED(1) BSD General Commands Manual SED(1)
N^HNA^HAM^HME^HE
s^Hse^Hed^Hd -- stream editor
to convert a man-page to text without the ^H-stuff. have a look on http://www.schweikhardt.net/man_page_howto.html#q10
create a perl-Skript called strip-headers with the content:
#!/usr/bin/perl -wn
# make it slurp the whole file at once:
undef $/;
# delete first header:
s/^\n*.*\n+//;
# delete last footer:
s/\n+.*\n+$/\n/g;
# delete page breaks:
s/\n\n+[^ \t].*\n\n+(\S+).*\1\n\n+/\n/g;
# collapse two or more blank lines into a single one:
s/\n{3,}/\n\n/g;
# see what is left...
print;
change the rights on the perl-script chmod 750 strip-headers and run it with:
man sed | ./strip-headers | col -bx > sed.man
or
man sed | ./strip-headers | col -bx | head -7 | grep sed
macOS man doesn't support the --ascii flag, so I used col -bx to strip the annoying formatting from man for piping into other commands.
man sed | col -bx | grep SYNOPSIS
col -b: Do not output any backspaces, printing only the last character written to each column position.
col -x: Output multiple spaces instead of tabs.
Notes:
I've read that man is meant to detect whether you're piping to another command or into a file, etc, but that was not my experience. At least for man 1.6c, the default for macOS.
Solution using col: https://unix.stackexchange.com/a/15866
Thanks #Cyrus - I didn't know about hexdump
Thanks #Oliver Gaida - I didn't know cat and vi would show display differently
I am pretty new to bash scripting. I have my bash script below and I want to include an if statement when month (ij==09) equals 09 then "i" should be from 01 to 30. I tried several ways but did not work.
How can I include an if statement in the code below to achieve my task.? Any help is appreciated.
Thanks.
#!/bin/bash
for ii in 2007
do
for i in 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #Day of the Month
do
for ij in 09 10 # Month
do
for j in 0000 0100 0200 0300 0400 0500 0600 0700 0800 0900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000 2100 2200 2300
do
cdo cat DAS_0125_H.A${ii}${ij}${i}.${j}.002_var.nc outfile_${ii}${ij}${i}.nc
done
done
done
done
The smallest change is adding a continue for day 31 in month 9.
You must test "09" as a string (or as 10#09).
(I also changed cdo ... into echo cdo ...)
for ii in 2007
do
for i in 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #Day of the Month
do
for ij in 09 10 # Month
do
if [[ "${ij}" == "09" ]] && [[ "${i}" == "31" ]]; then continue; fi
for j in 0000 0100 0200 0300 0400 0500 0600 0700 0800 0900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000 2100 2200 2300
do
echo "cdo cat DAS_0125_H.A${ii}${ij}${i}.${j}.002_var.nc outfile_${ii}${ij}${i}.nc"
done
done
done
done
It would be easier to read when you use loop through the variales with a seq. You do not want to use `for ((i=1;i<=31;i++)) in view of the leading zeroes.
Also use verbose variable names.
for year in 2007
do
for day in {01..31} # Day of the Month
do
for month in {09,10} # Month
do
if [[ "${month}" == "09" ]] && [[ "${day}" == "31" ]]; then continue; fi
for hour in {00..23}
do
echo cdo cat DAS_0125_H.A${year}${month}${day}.${hour}00.002_var.nc outfile_${year}${month}${day}.nc
done
done
done
done
When the files already exist, you can consider
ls DAS_0125_H.A2007{09,10}{01..31}.{00..23}00.002_var.nc |
sed -r 's/.*([0-9]{8})/cdo cat & outfile_\1.nc/'
When this will show the commands you want, you can execute them by
source <(ls DAS_0125_H.A2007{09,10}{01..31}.{00..23}00.002_var.nc |
sed -r 's/.*([0-9]{8})/cdo cat & outfile_\1.nc/')
I have a some file with the following content
$ cat somefile
28 46 5d a2 26 7a 192 168 2 2
0 15 e c8 a8 a3 192 168 100 3
54 4 2b 8 c 26 192 168 20 3
As you can see the values in first six columns are represented in hex, the values in last four columns in decimal formats. I just want to add 0 to every single symbol hexidecimal value.
Thanks beforehand.
This one should work out for you:
while read -a line
do
hex=(${line[#]:0:6})
printf "%02x " ${hex[#]/#/0x}
echo ${line[#]:6:4}
done < somefile
Example:
$ cat somefile
28 46 5d a2 26 7a 192 168 2 2
0 15 e c8 a8 a3 192 168 100 3
54 4 2b 8 c 26 192 168 20 3
$ while read -a line
> do
> hex=(${line[#]:0:6})
> printf "%02x " ${hex[#]/#/0x}
> echo ${line[#]:6:4}
> done < somefile
28 46 5d a2 26 7a 192 168 2 2
00 15 0e c8 a8 a3 192 168 100 3
54 04 2b 08 0c 26 192 168 20 3
Here is a way with awk if that is an option:
awk '{for(i=1;i<=6;i++) if(length($i)<2) $i=0$i}1' file
Test:
$ cat file
28 46 5d a2 26 7a 192 168 2 2
0 15 e c8 a8 a3 192 168 100 3
54 4 2b 8 c 26 192 168 20 3
$ awk '{for(i=1;i<=6;i++) if(length($i)<2) $i=0$i}1' file
28 46 5d a2 26 7a 192 168 2 2
00 15 0e c8 a8 a3 192 168 100 3
54 04 2b 08 0c 26 192 168 20 3
Please try this too, if it helps (bash version 4.1.7(1)-release)
#!/bin/bash
while read line;do
arr=($line)
i=0
for num in "${arr[#]}";do
if [ $i -lt 6 ];then
if [ ${#num} -eq 1 ];then
arr[i]='0'${arr[i]};
fi
fi
i=$((i+1))
done
echo "${arr[*]}"
done<your_file
This might work for you (GNU sed):
sed 's/\b\S\s/0&/g' file
Finds a single non-space character and prepends a 0.
I am trying to remove leading zeroes from a BASH array... I have an array like:
echo "${DATES[#]}"
returns
01 02 02 03 04 07 08 09 10 11 13 14 15 16 17 18 20 21 22 23
I'd like to remove the leading zeroes from the dates and store back into array or another array, so i can iterate in another step... Any suggestions?
I tried this,
for i in "${!DATES[#]}"
do
DATESLZ["$i"]=(echo "{DATES["$i"]}"| sed 's/0*//' )
done
but failed (sorry, i'm an old Java programmer who was tasked to do some BASH scripts)
Use parameter expansion:
DATES=( ${DATES[#]#0} )
With bash arithmetic, you can avoid the octal woes by specifying your numbers are base-10:
day=08
((day++)) # bash: ((: 08: value too great for base (error token is "08")
((day = 10#$day + 1))
echo $day # 9
printf "%02d\n" $day # 09
You can use bash parameter expansion (see http://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html) like this:
echo ${DATESLZ[#]#0}
If: ${onedate%%[!0]*} will select all 0's in front of the string $onedate.
we could remove those zeros by doing this (it is portable):
echo "${onedate#"${onedate%%[!0]*}"}"
For your case (only bash):
#!/bin/bash
dates=( 01 02 02 08 10 18 20 21 0008 00101 )
for onedate in "${dates[#]}"; do
echo -ne "${onedate}\t"
echo "${onedate#"${onedate%%[!0]*}"}"
done
Will print:
$ script.sh
01 1
02 2
02 2
08 8
10 10
18 18
20 20
21 21
0008 8
00101 101
How to add leading zero to bash range?
For example, I need cycle 01,02,03,..,29,30
How can I implement this using bash?
In recent versions of bash you can do:
echo {01..30}
Output:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
Or if it should be comma separated:
echo {01..30} | tr ' ' ','
Which can also be accomplished with parameter expansion:
a=$(echo {01..30})
echo ${a// /,}
Output:
01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
another seq trick will work:
seq -w 30
if you check the man page, you will see the -w option is exactly for your requirement:
-w, --equal-width
equalize width by padding with leading zeroes
You can use seq's format option:
seq -f "%02g" 30
A "pure bash" way would be something like this:
echo {0..2}{0..9}
This will give you the following:
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Removing the first 00 and adding the last 30 is not too hard!
This works:
printf " %02d" $(seq 1 30)