Edit: SOLVED. Thank you all for your incredible insights! All of you helped me improve my code and knowledge! Special thanks to @Quibblekrust@thelemmy.club who just NAILED it. :)

I’m playing around with Bash just to learn.

LIST=$(ls); for i in $LIST; do echo "I found one!"; done

The variable “i” could literally be anything, as long as it doesn’t have a special meaning for Bash, in which case I’d have to escape it, right? Anyway, my real question is: how does do (or rather the whole for-expression) know that “i” here means “for every line/item that ls outputs”? The above one liner works great and writes “I found one!” the number of times corresponding to the number of lines or items that ls outputs. But I would like to understand why it worked…

I’m a complete beginner at both Bash and C, but I understand some basic concepts.

  • Quibblekrust@thelemmy.club
    link
    fedilink
    English
    arrow-up
    4
    ·
    edit-2
    4 days ago

    for loops

    Your code executes ls and records the results in a variable. The result is some text, a string of characters. (We call them “strings” and i is now a string variable.) Among the characters in a string variable might be spaces, tabs, or new line characters. I mention this because the special variable IFS is used by for loops, and it contains exactly one space, tab, and new line by default.

    When you call for with a string as the input, it splits the string into units by splitting on each character in IFS. That is, it splits the big string into individual parts by splitting at each space, tab and new line. So this creates an array which is what is looped over. Each word in turn is assigned to your looping variable and then the code after the do is executed once per word.

    (“word” has a sort of a special meaning here. When I say word, I mostly just mean a string that has no spaces in it. When you read text in English, there are words. They’re strings of characters separated by spaces. But words can also be separated by tabs, new lines, commas, semicolons, or whatever, but not by default when using for! You have to modify IFS to add those characters if you want them to be considered word separators.)

    So, if any of the file system entries returned by ls have spaces in them, your loop is going to create more outputs than there are file system entries in the current directory.

    For example:

    file one.txt
    file two.txt
    my photo.jpg
    notes (final).md
    a b c d.txt
    

    That would cause like 12 loops and 12 outputs in your code despite there only being five files.

    If you instead overwrite IFS before running your loop, and only assign a single new line to the variable, then your loop will only be over the actual lines of the input text. Like this:

    IFS=$'\n'

    and then use your exact code above. Using my example of five files, this code will now only produce 5 outputs, not 12.

    You can assign whatever characters you want to IFS.

    (I have not tested any of this code, or examples.)

    Variable names

    The loop variable name i is just an identifier. Any valid variable name would work except you can’t use the reserved names like $1, $2 or any keywords as names. Also, there’s no way to escape an identifier. They are just literal names.

    You also don’t want to use any built-in variable names or else you’ll overwrite their values for the duration of the current session. Bash will happily let you use them as your looping variable, but the rest of your code might have undesirable results. Variable names like IFS, for instance. :D

    • emotional_soup_88@programming.devOP
      link
      fedilink
      English
      arrow-up
      2
      ·
      edit-2
      4 days ago

      This about the IFS variable was eye opening! Thank you SO much! This is exactly what I was trying to understand, namely, how on earth the for-loop is smart enough to understand how to count when I haven’t specified a numerical interval (as I do in for instance C when I practice that). This just solved it all. Thanks! Now I also understand why my code gave me excessive outputs when I changed ls into ls -l. The IFS variable made the for-loop count every single blank space!!! :D

    • rycee@lemmy.world
      link
      fedilink
      arrow-up
      1
      ·
      4 days ago

      For maximum pedantry, it may be worth mentioning that filenames in typical Linux file systems can contain newline characters.