Shell What is the best practice to learn SHELL/BASH

Hello, I would like to know how to learn shell/bash scripting to do evryday tasks of iterating lists and parsing files. I have access to both linux and freebsd machines, and I would like to know all diferences in both sh in freebsd and these bash in linux. Is there anything good on the web? For me, it's impossible to even write a script that will iterate the file adding each pice of text separated by a empty line inside array and the print it out with indexes
 
I would like to know how to learn shell/bash scripting to do evryday tasks of iterating lists and parsing files.
Start doing it. You only get the experience when you actually do the work. Theory is nice to have but you really only learn how to do things when you're actually doing them. My first scripts were pretty shitty, I didn't know any better. Experience has taught me that there are sometimes better ways of solving a specific problem. That's something you have to find out.

I have access to both linux and freebsd machines, and I would like to know all diferences in both sh in freebsd and these bash in linux.
Start doing it, then you'll run into those implementation details. For the most part you don't need to worry about it.

Is there anything good on the web?
This is a good place to start: https://www.grymoire.com/Unix/Sh.html

For me, it's impossible to even write a script that will iterate the file adding each pice of text separated by a empty line inside array and the print it out with indexes
Arrays are a typical bash construct. Stick to standard POSIX shell. If you can't hack it with a shell script use Python or some other, more advanced, scripting language. Or, if you insist on using bash specifics, install shells/bash. Don't assume that a bash(1) script will always run on sh(1).
 
I have read the grymoire.com cover to cover, with exception of "/Csh.html" and "/Bourne.html".
On DOS/Windows, I used to use BATCH. Then I moved to perl. First Activeperl, then Strawberry.
On FreeBSD, I tried to re-write my BATCH/perl scripts in sh(1). Some of them work, some didn't.
But it helped me to learn how things work. I recommend to use sh everywhere, *BSD and Linux.
 
There's plenty of pitfalls to learning shell scripting. I have quite a bit of experience on FreeBSD (Since 2017) and Linux (Since 2002), but even now, I don't have a great handle on shell scripting, and need to ask for help:
To top this off:

It does take paying attention to how you order things. For example:
Code:
find /usr/ports/clean_backup -name Makefile -exec grep "PKGNAMEPREFIX=" {} "+" | grep -w "qt5-" | cut -d/ -f5-6 | grep -v py-qt5 >> all_qt5_ports.txt

This finds all Qt 5 ports in the ports tree, and creates a list you can feed to Poudriere. But one pitfall is how you order the commands you pipe from and to. The first two greps work on the Makefile. But the last grep needs to work on the entire command's output before tacking it onto the end of all_qt5_ports.txt.

Not impossible to just look for directories that start with "/qt5-", but that requires knowing how to use find(1) to look for directories, and to filter out what you don't need. Also - I thought I could use distinfo rather than Makefile - but then I discovered that some QT ports don't have distinfo!
 
Start doing it. You only get the experience when you actually do the work.
Yes, this times infinity or divide by zero. Shells are "language". Languages are best learn by doing, even spoken language. Start out with Hello World! and then get more complex.
 
The thing is that I'm actually trying to "express my thoughts" on this "language", but still can't done anything with IT. So my specific question is:

How to iterate file and put parts of text separated by empty line in the array and print this array along with indexes. It's really kicks me of crying on keyboard, I've learnd about IFS already, but can't put all the pices together to start just parsing log files along, googling is not helping at all, don't know why.
 
POSIX shell doesn't have arrays. Bash does. So you're going to need to figure out a different strategy or use bash(1) instead of sh(1).
let's say instead of writing to the array i'll just print it out on the screen. It's the first defference in bash and you told me about, great not I know 1 more not only that this is just sh and bash in first line. Thank you
 
You didn't ask for it but i think looking into scripting languages like Perl or Python would be worth a try.
You can create very sophisticated programs with them. (And they do have arrays :cool:)
 
let's say instead of writing to the array i'll just print it out on the screen.
Something really simple:
Code:
dice@armitage:~/test % cat sometext.txt
This is some text
spread over multiple lines
Hello World!
dice@armitage:~/test % cat read.sh
#!/bin/sh

while read LINE; do
  echo $LINE
done < sometext.txt
dice@armitage:~/test % ./read.sh
This is some text
spread over multiple lines
Hello World!
 
If sh isn't sufficient have a look at other standard tools - for pure text-bashing awk(1) is usually the "right tool for the job". It can be easily combined/embedded in shell scripts and is present on all UNIXes (illumos/solaris), BSDs and unix-like systems like linux. (but don't use gnu awk as a reference).
Also awk is still _MUCH_ faster than perl or python - which would be the next escalation if awk isn't sufficient anymore. But both are not present on many default/base installations, so if you aim for portability, try to stick to sh + awk.

As for bash: don't use it. It is a linux-phenomenon but not found in the default installaiton on any other OS. For portable scripts always stick to POSIX shell (sh).
 
Code:
#!/bin/sh
IFS="
"
i=0
while read line
do
eval VAR_$i=\"$line\"
i=$(($i+1))
done
j=0
while [ $j -lt $i ]
do
 eval line="\$VAR_$j"
 echo  $j "=>" $line
 j=$(($j+1))
done
sh a.sh < /etc/profile
Code:
0 => # : releng/12.2/bin/sh/profile 363525 2020-07-25 11:57:39Z pstef $
1 => #
2 => # System-wide .profile file for sh(1).
3 => #
4 => # For the setting of languages and character sets please see
5 => # login.conf(5) and in particular the charset and lang options.
6 => # For full locales list check /usr/share/locale/*
7 => # You should also read the setlocale(3) man page for information
8 => # on how to achieve more precise control of locale settings.
9 => #
10 => # Check system messages
11 => # msgs -q
12 => # Allow terminal messages
13 => # mesg y
 
You didn't ask for it but i think looking into scripting languages like Perl or Python would be worth a try.
You can create very sophisticated programs with them. (And they do have arrays :cool:)
It wont help in the case when the environment wont allow me to install php or perl, and as far as I know Perl now is the part of Linux, I really wan't to have comfort wiht shells, couse I think one time I won't be able to use something else.
 
I'm frankly a beginner in sh(1), but for 'arrays to iterate through' type of things, I generate and clean up a simple text list. I know what an array is, and I can say that for a quick-and-dirty task like 'generating a simple text list using sh(1)', it's overkill. If you have a complex port (like a KDE app), then you're better off using C/C++, and then using arrays makes more sense. It's not impossible to do pretty fancy stuff using sh(1) (just look at bectl(8), written by vermaden ), but it's important to be able to pick the right tool for the job. I think sh(1) and arrays don't mix that well, especially for beginners like me.
 
Hello, I would like to know how to learn shell/bash scripting to do evryday tasks of iterating lists and parsing files. I have access to both linux and freebsd machines, and I would like to know all diferences in both sh in freebsd and these bash in linux. Is there anything good on the web? For me, it's impossible to even write a script that will iterate the file adding each pice of text separated by a empty line inside array and the print it out with indexes
Do not use bash(1) for script. Use POSIX /bin/sh which works everywhere and is portable.

Here is some really basic guide for a start:
 
How to iterate file and put parts of text separated by empty line in the array and print this array along with indexes.

scriptFile

Bash:
#!/bin/sh

# i: counter
# j: counter
# a: array
# l: line
# v: value

# read and write to an array!
i=0
while IFS= read l; do
        eval a_${i}=\$l
        i=`expr $i + 1`
done < "$1"

# print the array
j=0
while [ $j -lt $i ]; do
        eval v=\$a_${j}
        j=$((j + 1))
        printf "%*u: %s\n" "${#i}" $j $v
done

textFile
Code:
file1.txt
file2.txt
.
.
.
fileN.txt



$ chmod u+x scriptFile
$ ./scriptFile textFile
The outout:

Code:
1: file1.txt
2: file2.txt
.
.
.
N: fileN.txt

It wont help in the case when the environment wont allow me to install php or perl,

* awk and sed will help.
* Learn how word splitting, exapnsion, field splitting, globe, etc works:
* Learn how quoting works.
1. They are different.
2. They have different order.
3. There's few exception too.

Solve and write in C and/or POSIX Shell. Too much work? Then Perl 5.32.1 is your geometric mean.
 
vigole just to clarify for the OP, sh does not support arrays.
eval a_${i}=\$l

And as you know, in above section, you are not actually creating an array but instead creating bunch of variables.

Basically you are assigning values to variables which has numbers in their names.

Kind of illusion of array.
 
vigole just to clarify for the OP, sh does not support arrays.


And as you know, in above section, you are not actually creating an array but instead creating bunch of variables.

Basically you are assigning values to variables which has numbers in their names.

Kind of illusion of array.
you can quickly access them by the index so it's as close to an array as it gets
 
I often use simple lists and awk to simulate array-like structures in sh(1) -- a simple example:
Code:
#!/bin/sh
list="a b c d e foo g"
listcount=$(echo $list | awk 'BEGIN{FS=" "}{print NF}')
echo 'List="'$list'"'
echo "Element count="$listcount
echo "A random element access:"
el=$(echo $list | awk 'BEGIN{FS=" "}{print $6}')
echo "Element 6 = "$el
echo "Iteration:"
i=0; for x in $list; do
  i=$(($i+1))
  echo "Element "$i" = "$(echo $list | awk 'BEGIN{FS=" "}{print $'$i'}')
done
 
Code:
#!/bin/sh
list="a b c d e foo g"
set -- $list
listcount=$#
echo 'List="'$list'"'
echo "Element count="$listcount
echo "A random element access:"
echo "Element 6 = $6"
echo "Iteration:"
i=0; for x in $list; do
  i=$(($i+1))
  eval echo "Element $i" = \$$i
done
 
Back
Top