简体   繁体   中英

Bash- Count Newlines via terminal command

i'm trying to make a bash script that counts the newlines in an input. The first if statement (switch $0) works fine but the problem I'm having is trying to get it to read the WC of a file in a terminal argument.

eg ~$ ./script.sh

1

2

3

4

(User presses CTRL+D)

display word count here # answer is 5 - works fine

eg ~$ .script1.sh < script1.sh

WC here -(5)

~$ succesfully redirects the stdin from a file

but eg ~$ ./script1.sh script1.sh script2.sh

WC displayed here for script1.sh

WC displayed here for script2.sh

NOTHING

~$

the problem I believe is the second if statement, instead of executing the script in the terminal it goes to the if statement and waits for a user input and its not giving back the echo statement.

Any help would be greatly appreciated since I cannot figure out why it won't work without the ~$ < operator.

#!/bin/bash
#!/bin/sh

read filename ## read provided filename
USAGE="Usage: $0 $1 $2..." ## switch statement

if [ "$#" == "0" ]; then  
    declare -i lines=0 words=0 chars=0
    while IFS= read -r line; do
    ((lines++))
    array=($line)               
    ((words += ${#array[@]}))
    ((chars += ${#line} + 1))   # add 1 for the newline
done < /dev/stdin
fi
echo "$lines $words $chars $filename" ## filename doesn't print, just filler

### problem if statement####
if [ "$#" != "0" ]; then # space between [] IS VERY IMPORTANT
    declare -i lines=0 words=0 chars=0
    while IFS= read -r line; do
    lines=$( grep -c '\n'<"filename") ##should use grep -c to compare only new lines in the filename. assign to variable line
    words=$( grep -c '\n'<"filename")
    chars=$( grep -c '\n'<"filename")
echo "$lines $words $chars"
 #lets user press CTRL+D to end script and count the WC
fi

Use wc .

Maybe the simplest is to replace the second if block with a for .

$: cat tst
#! /bin/env bash
declare -i lines=0 words=0 chars=0
case "$#" in
0)  wc ;;
*)  for file in $*
    do  read lines words chars x <<< "$( wc $file )"
        echo "$lines $words $chars $file"
    done ;;
esac
$: cat foo

hello
world
and
goodbye cruel world!

$: tst < foo
 6  6 40
$: tst foo tst
6 6 40 foo
9 38 206 tst
#!/bin/sh
set -e
if [ -t 0 ]; then
    # We are *not* reading stdin from a pipe or a redirection.
    # Get the counts from the files specified on the cmdline
    if [ "$#" -eq 0 ]; then
        echo "no files specified" >&2
        exit 1
    fi
    cat "$@" | wc
else
    # stdin is attached to a pipe or redirected from a file
    wc
fi | { read lines words chars; echo "lines=$lines words=$words chars=$chars"; }

The variables from the read command only exist within the braces, due to the way the shell (some shells anyway) use subshells for commands in a pipeline. Typically, the solution for that is to redirect from a process substitution (bash/ksh).

This can be squashed down to

#!/bin/bash
[[ -t 0 ]] && files=true || files=false
read lines words chars < <({ ! $files && cat || cat "$@"; } | wc) 
echo "lines=$lines words=$words chars=$chars"

a very quick demo of cmd | read x cmd | read x versus read x < <(cmd)

$ x=foo; echo bar | read x; echo $x
foo
$ x=foo; read x < <(echo bar); echo $x
bar

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM