Shell

The Bash For Loop

July 15, 2017 Linux, Programming, Shell No comments

This week at work, I had been tasked to copy a directory in Linux 6 times, all with different names.

This, of course, is not that directory, but let’s pretend it is.

We want 6 copies of this, one for each employee

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Easy enough, just cp -r the directory 6 times, right?  Well, you could, but we always strive to be as clever and efficient as possible.

Let’s say for this example we need a copy of this directory for all 6 employees: David, Mark, Jeremy, Clyde, Warren, and Sam.

well, that won’t work

 

 

 

 

 

There’s a lot of things that surprisingly won’t work, mostly because the directories don’t exist.  We could make them, or, as my boss showed me, we could use a little for loop magic.

surprisingly simple!

 

 

 

 

Is it really all there?  Yes!  This is truncated, but you can see these users have a clone of the original directory.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

So how do I use this?

Well, the items you want to iterate through go after for x in

The command you want to execute goes after do and you use the variable $x to iterate through the items.

If we wanted to create 10 files named each letter of the alphabet, we would use

for x in a b c d e f g h i j; do touch $x; done

and you will have those files.  You can remove them with an equally simple line:

for x in a b c d e f g h i j; do rm $x; done

Shelled-out Commands

May 20, 2017 Programming, Shell No comments

So I remember shelling out commands in Java, and it was quite a process.  It turns out it’s essentially the same for Go.

In interpreted scripting languages like python or ruby, it’s not really something you have to put any thought into.


So because bash is a shell you don’t have to do anything.

#!/bin/bash

ls -l

Ruby supports backticks.

#!/usr/bin/env ruby

`ls -l`

In Python, you can call os.system()

#!/usr/bin/env python
import os

if __name__ == "__main__":
    os.system("ls -l")

So what about real programming languages? Well, there’s a little more involved to shell something out if you’re expecting the output much like an actual shell.


Using Go, I wrote this to shell out a command.

package main

import (
    "os/exec"
    "fmt"
    "bufio"
    "os"
)

func main() {
    // create command and arguments
    cmd := "ls"
    args := []string { "-l" }
    command := exec.Command(cmd, args...)

    // connect to stdout and stderr
    stdOutReader, err := command.StdoutPipe()
    checkErr("Error creating stdout pipe", err)
    stdErrReader, err := command.StderrPipe()
    checkErr("Error creating stderr pipe", err)

    stdOutScanner := bufio.NewScanner(stdOutReader)
    stdErrScanner := bufio.NewScanner(stdErrReader)

    go func() {
        for stdOutScanner.Scan() {
            fmt.Printf("%s\n", stdOutScanner.Text())
        }
    }()
    go func() {
        for stdErrScanner.Scan() {
            fmt.Printf("%s\n", stdErrScanner.Text())
        }
    }()

    // run the command
    err = command.Start()
    checkErr("Error starting command", err)

    err = command.Wait()
    checkErr("Error waiting for command", err)
}

func checkErr(msg string, err error) {
    if err != nil {
        fmt.Fprintln(os.Stderr, msg, err)
        os.Exit(1)
    }
}

Here’s the output of that program compared to ls -l

 

 

 

 

 


In reality, when simply using ls, we could have probably ignored stderr.  If you want stderr messages though, the code above will provide that output as well.