I'm trying to write a script to add the name of a file and the directory path to a text file on a single line.
E.g.
Filename /root/folder/folder/
I've tried:
ls "$1" >> /root/folder/folder/file.txt
pwd >> /root/folder/folder/file.txt
But it shows up on seperate lines.
I did try
ls "$1" && pwd >> ......
but it only pasted the pwd to the text file.
I'm new to Linux so any help would be greatly appreciated.
How about this:
echo "$1 $(pwd)" >> outputfile
Use:
p1=`ls $1`
p2=`pwd`
echo $p1 $p2 >> /root/folder/folder/file.txt
Try:
echo "$1$(pwd)" >> /root/folder/folder/file.txt
Or you can create a new variable with the concatenated string and then echo that:
NewVar="$1$(pwd)"
echo "$NewVar" >> /root/folder/folder/file.txttxt
In the first example you have above, each command inserts a newline. In the second example, the && that you have breaks up the two commands so that the output of the first is not redirected. You can't distribute redirection across the &&, in other words.
A common technique is to use printf (or echo -n, but that's less portable) to avoid writing the newline:
exec >> /root/folder/folder/file.txt
printf "%s " "$1"
pwd
In your example, you use a command to generate the initial data, and a good technique in that case is to use tr to trim the newline
ls "$1" | tr -d '\012'
printf " " # Insert a space
pwd
Related
never worked with shell scripts before,but i need to in my current task.
So i have to run a command that returns output like this:
awd54a7w6ds54awd47awd refs/heads/SomeInfo1
awdafawe23413f13a3r3r refs/heads/SomeInfo2
a8wd5a8w5da78d6asawd7 refs/heads/SomeInfo3
g9reh9wrg69egs7ef987e refs/heads/SomeInfo4
And i need to loop over every line of output get only the "SomeInfo" part and write it to a file in a format like this:
["SomeInfo1","SomeInfo2","SomeInfo3"]
I've tried things like this:
for i in $(some command); do
echo $i | cut -f2 -d"heads/" >> text.txt
done
But i don't know how to format it into an array without using a temporary file.
Sorry if the question is dumb and probably too easy and im sure i can figure it out on my own,but i just don't have the time for it because its just an extra conveniance feature that i personally want to implement.
Try this
# json_encoder.sh
arr=()
while read line; do
arr+=(\"$(basename "$line")\")
done
printf "[%s]" $(IFS=,; echo "${arr[*]}")
And then invoke
./your_command | json_encoder.sh
PS. I personally do this kind of data massaging with Vim.
Using Perl one-liner
$ cat petar.txt
awd54a7w6ds54awd47awd refs/heads/SomeInfo1
awdafawe23413f13a3r3r refs/heads/SomeInfo2
a8wd5a8w5da78d6asawd7 refs/heads/SomeInfo3
g9reh9wrg69egs7ef987e refs/heads/SomeInfo4
$ perl -ne ' { /.*\/(.*)/ and push(#res,"\"$1\"") } END { print "[".join(",",#res)."]\n" }' petar.txt
["SomeInfo1","SomeInfo2","SomeInfo3","SomeInfo4"]
While you should rarely ever use a script to format json, in your case you are simply parsing output into a comma-separated line with added end-caps of [...]. You can use bash parameter expansion to avoid spawning any additional subshells to obtain the last field of information in each line as follows:
#!/bin/bash
[ -z "$1" -o ! -r "$1" ] && { ## validate file given as argument
printf "error: file doesn't exist or not readable.\n" >&2
exit 1
}
c=0 ## simple flag variable
while read -r line; do ## read each line
if [ "$c" -eq '0' ]; then ## is flag 0?
printf "[\"%s\"" "${line##*/}" ## output ["last"
else ## otherwise
printf ",\"%s\"" "${line##*/}" ## output ,"last"
fi
c=1 ## set flag 1
done < file ## redirect file to loop
echo "]" ## append closing ]
Example Use/Output
Using your given data as the input file, you would get the following:
$ bash script.sh file
["SomeInfo1","SomeInfo2","SomeInfo3","SomeInfo4"]
Look things over and let me know if you have any questions.
You can also use awk without any loops I guess:
cat prev_output | awk -v ORS=',' -F'/' '{print "\042"$3"\042"}' | \
sed 's/^/[/g ; s/,$/]\n/g' > new_output
cat new_output
["SomeInfo1","SomeInfo2","SomeInfo3","SomeInfo4"]
The task is to read a file containing letters and numbers. Then put the letters in a text file inside a Letters directory and the numbers in a different text file in a numbers directory. The problem I'm having is I don't know how I would identify strings and integers within a file.
The file I'm using contains:
723
Xavier
Cat
323
Zebra
This is the code I've come up with.
#!/bin/bash
file = $1
mkdir Numbers
mkdir Letters
touch Numbers/Numbers.txt
touch Letters/Letters.txt
for x in $file; do
if [x == string];
then
echo x >> Letters/Letters.txt;
fi
if [x == integer];
then
echo x >> Numbers/Numbers.txt;
fi
Your original code has a lot of syntactical errors. I suggest going forward please submit a code which is at least syntactically correct.
Anyways, assuming you are new to unix shell scripting, here a quick solution to your problem
#!/bin/bash
file=$1
mkdir Numbers
mkdir Letters
touch Numbers/Numbers.txt
touch Letters/Letters.txt
while read line
do
if [[ $line =~ [^0-9] ]]
then
echo $line >> Letters/Letters.txt
else
echo $line >> Numbers/Numbers.txt
fi
done < $file
The question is vague.
This solves the question as posted, and handles lines with mixed
numbers and strings, (e.g.: "a1b2c3".), using tee and bash
process substitution, with tr to delete all undesired chars:
mkdir Numbers Letters
tee >(tr -d '[0-9]' > Letters/Letters.txt) \
>(tr -d '[a-zA-Z]' > Numbers/Numbers.txt) \
> /dev/null < inputfile
If the goal is to remove whole lines that are either all numbers
or all letters, try grep:
mkdir Numbers Letters
tee >(grep -v '^[0-9]*$' > Letters/Letters.txt) \
>(grep '^[0-9]*$' > Numbers/Numbers.txt) \
> /dev/null < inputfile
I have to write a script in linux that saves one line of text to a file and then appends a new line. What I currently have is something like:
read "This line will be saved to text." Text1
$Text1 > $Script.txt
read "This line will be appended to text." Text2
$Text2 >> $Script.txt
One of the main benefits of scripting is that you can automate processes. Using
read like you have destroys that. You can accept input from the user without
losing automation:
#!/bin/sh
if [ "$#" != 3 ]
then
echo 'script.sh [Text1] [Text2] [Script]'
exit
fi
printf '%s\n' "$1" "$2" > "$3"
Assuming you don't mind if the second line of your output file is overwritten (not appended) every time the script is run; this might do.
#!/bin/sh
output_file=output.dat
if [ -z "$1" ] || [ -z "$2" ]; then echo Need at least two arguments.; fi
line1=$1; line2=$2
echo $line1 > $output_file
echo $line2 >> $output_file
Executing the script:
# chmod +x foo.sh
# ./foo.sh
Need at least two arguments.
# ./foo.sh hello world
# cat output.dat
hello
world
I need a simple shell program which has to do something like this:
script.sh word_to_find file1 file2 file3 .... fileN
which will display
word_to_find 3 - if word_to_find appears in 3 files
or
word_to_find 5 - if word_to_find appears in 5 files
This is what I've tried
#!/bin/bash
count=0
for i in $#; do
if [ grep '$1' $i ];then
((count++))
fi
done
echo "$1 $count"
But this message appears:
syntax error: "then" unexpected (expecting "done").
Before this the error was
[: grep: unexpected operator.
Try this:
#!/bin/sh
printf '%s %d\n' "$1" $(grep -hm1 "$#" | wc -l)
Notice how all the script's arguments are passed verbatim to grep -- the first is the search expression, the rest are filenames.
The output from grep -hm1 is a list of matches, one per file with a match, and wc -l counts them.
I originally posted this answer with grep -l but that would require filenames to never contain a newline, which is a rather pesky limitation.
Maybe add an -F option if regular expression search is not desired (i.e. only search literal text).
The code you showed is:
#!/bin/bash
count=0
for i in $#; do
if [ grep '$1' $i ];then
((count++))
fi
done
echo "$1 $count"
When I run it, I get the error:
script.sh: line 5: [: $1: binary operator expected
This is reasonable, but it is not the same as either of the errors reported in the question. There are multiple problems in the code.
The for i in $#; do should be for i in "$#"; do. Always use "$#" so that any spaces in the arguments are preserved. If none of your file names contain spaces or tabs, it is not critical, but it is a good habit to get into. (See How to iterate over arguments in bash script for more information.)
The if operations runs the [ (aka test) command, which is actually a shell built-in as well as a binary in /bin or /usr/bin. The use of single quotes around '$1' means that the value is not expanded, and the command sees its arguments as:
[
grep
$1
current-file-name
]
where the first is the command name, or argv[0] in C, or $0 in shell. The error I got is because the test command expects an operator such as = or -lt at the point where $1 appears (that is, it expects a binary operator, not $1, hence the message).
You actually want to test whether grep found the word in $1 in each file (the names listed after $1). You probably want to code it like this, then:
#!/bin/bash
word="$1"
shift
count=0
for file in "$#"
do
if grep -l "$word" "$file" >/dev/null 2>&1
then ((count++))
fi
done
echo "$word $count"
We can negotiate on the options and I/O redirections used with grep. The POSIX grep
options -q and/or -s options provide varying degrees of silence and -q could be used in place of -l. The -l option simply lists the file name if the word is found, and stops scanning on the first occurrence. The I/O redirection ensures that errors are thrown away, but the test ensures that successful matches are counted.
Incorrect output claimed
It has been claimed that the code above does not produce the correct answer. Here's the test I performed:
$ echo "This country is young" > young.iii
$ echo "This country is little" > little.iii
$ echo "This fruit is fresh" > fresh.txt
$ bash findit.sh country young.iii fresh.txt little.iii
country 2
$ bash -x findit.sh country young.iii fresh.txt little.iii
+ '[' -f /etc/bashrc ']'
+ . /etc/bashrc
++ '[' -z '' ']'
++ return
+ alias 'r=fc -e -'
+ word=country
+ shift
+ count=0
+ for file in '"$#"'
+ grep -l country young.iii
+ (( count++ ))
+ for file in '"$#"'
+ grep -l country fresh.txt
+ for file in '"$#"'
+ grep -l country little.iii
+ (( count++ ))
+ echo 'country 2'
country 2
$
This shows that for the given files, the output is correct on my machine (Mac OS X 10.10.2; GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin14)). If the equivalent test works differently on your machine, then (a) please identify the machine and the version of Bash (bash --version), and (b) please update the question with the output you see from bash -x findit.sh country young.iii fresh.txt little.iii. You may want to create a sub-directory (such as junk), and copy findit.sh into that directory before creating the files as shown, etc.
You could also bolster your case by showing the output of:
$ grep country young.iii fresh.txt little.iii
young.iii:This country is young
little.iii:This country is little
$
#!/usr/bin/perl
use strict;
use warnings;
my $wordtofind = shift(#ARGV);
my $regex = qr/\Q$wordtofind/s;
my #file = ();
my $count = 0;
my $filescount = scalar(#ARGV);
for my $file(#ARGV)
{
if(-e $file)
{
eval { open(FH,'<' . $file) or die "can't open file $file "; };
unless($#)
{
for(<FH>)
{
if(/$regex/)
{
$count++;
last;
}
}
close(FH);
}
}
}
print "$wordtofind $count\n";
You could use an Awk script:
#!/usr/bin/env awk -f
BEGIN {
n=0
} $0 ~ w {
n++
} END {
print w,n
}
and run it like this:
./script.awk w=word_to_find file1 file2 file3 ... fileN
or if you don't want to worry about assigning a variable (w) on the command line:
BEGIN {
n=0
w=ARGV[1]
delete ARGV[1]
} $0 ~ w {
n++
} END {
print w,n
}
I would like to write a shell script in which I'll take a line input from user,which will contain some xxx.cpp as filename.
I want to get that "xxx" in another variable.
e.g.
if user give input as:
some params path/to/file/xyz.cpp more p/ara/ms
I want to get xyz which occurs before".cpp" and after last occurance of "/" before ".cpp"
Use basename [param] [.ext].
echo `basename $1 .cpp`
where 1 is the index of path/to/file.xyz in the argument list.
Here's a sample bash script to prompt the user for a list of files and filter all the input and display only the base name of files that end in '.cpp' (with .cpp removed) and which are readable
#!/bin/bash
echo Enter list of files:
read list_of_files
for file in $(echo $list_of_files); do
if echo $file | grep '\.cpp$' > /dev/null; then
if [[ -r $file ]]; then
fn=$(basename ${file})
fn=${fn%.*}
echo $fn
fi
fi
done