Home Php C# Sql C C++ Javascript Python Java Go Android Git Linux Asp.net Django .net Node.js Ios Xcode Cocoa Iphone Mysql Tomcat Mongodb Bash Objective-c Scala Visual-studio Apache Elasticsearch Jar Eclipse Jquery Ruby-on-rails Ruby Rubygems Android-studio Spring Lua Sqlite Emacs Ubuntu Perl Docker Swift Amazon-web-services Svn Html Ajax Xml Java-ee Maven Intellij-idea Rvm Macos Unix Css Ipad Postgresql Css3 Json Windows-server Vue.js Typescript Oracle Hibernate Internet-explorer Github Tensorflow Laravel Symfony Redis Html5 Google-app-engine Nginx Firefox Sqlalchemy Lucene Erlang Flask Vim Solr Webview Facebook Zend-framework Virtualenv Nosql Ide Twitter Safari Flutter Bundle Phonegap Centos Sphinx Actionscript Tornado Register | Login | Edit Tags | New Questions | 繁体 | 简体


10 questions online user: 21

150
votes
answers
24 views
+10

How can I send an email through the UNIX mailx command?

How can I send an email through the UNIX mailx command?

沙发
+1000

an example

$ echo "something" | mailx -s "subject" recipient@somewhere.com

to send attachment

$ uuencode file file | mailx -s "subject" recipient@somewhere.com

and to send attachment AND write the message body

$ (echo "something
" ; uuencode file file) | mailx -s "subject" recipient@somewhere.com

我嘗試過但是沒有回應。它既沒有給出一些錯誤信息,也沒有發送郵件到myname@gmail.com。是否需要任何服務器配置? - user269484 2010年2月18日4:58

不需要任何配置。檢查您的互聯網連接。我通過電纜直接連接到互聯網,我不使用代理或任何東西,所以它在我這邊工作。 - ghostdog74 2010年2月18日5:58

您還應該檢查收件箱中的錯誤消息。即運行郵件。 - hafichuk 12年12月31日在5:52

但請注意,uuencode是過去千年的遺留技術,它不會產生我們今天所說的“附件”。它基本上將一個機器可讀的混亂放在消息文本的末尾。在這個時代,正確的MIME感知郵件服務器可以為您提供更好的服務。不幸的是,沒有普遍支持的mailx替換MIME功能,但如果你有mutt,那可能是阻力最小的路徑。 - 2014年10月1日3:18

@user269484 Gmail doesn't accept email from unauthorised IP addresses. Read support.google.com/mail/answer/10336 – Manas Jayanth Jan 11 '16 at 18:07

板凳
+270

Here you are :

echo "Body" | mailx -r "FROM_EMAIL" -s "SUBJECT" "To_EMAIL"

PS. Body and subject should be kept within double quotes. Remove quotes from FROM_EMAIL and To_EMAIL while substituting email addresses.

On Mac you will receive an error from the mailx command if you use -r mailx: illegal option -- r Usage: mailx [-EiInv] [-s subject] [-c cc-addr] [-b bcc-addr] [-F] to-addr ... mailx [-EHiInNv] [-F] -f [name] mailx [-EHiInNv] [-F] [-u user] mailx -e [-f name] mailx -H – jcpennypincher Apr 15 '16 at 19:36

you could do -S from=a@b.com – Kalpesh Soni Jun 8 '17 at 18:54

地板
+60
mailx -s "subjec_of_mail" abc@domail.com < file_name

through mailx utility we can send a file from unix to mail server. here in above code we can see first parameter is -s "subject of mail" the second parameter is mail ID and the last parameter is name of file which we want to attach

This doesn't attach the file, it puts the content of the file into the body – Guus Dec 17 '18 at 16:58

4楼
+50

Its faster with MUTT command

echo "Body Of the Email"  | mutt -a "File_Attachment.csv" -s "Daily Report for $(date)"  -c cc_mail@g.com to_mail@g.com -y
  1. -c email cc list
  2. -s subject list
  3. -y to send the mail
5楼
+40

From the man page:

Sending mail

To send a message to one or more people, mailx can be invoked with arguments which are the names of people to whom the mail will be sent. The user is then expected to type in his message, followed by an ‘control-D’ at the beginning of a line.

In other words, mailx reads the content to send from standard input and can be redirected to like normal. E.g.:

ls -l $HOME | mailx -s "The content of my home directory" someone@email.adr
6楼
+40
mail [-s subject] [-c ccaddress] [-b bccaddress] toaddress

-c and -b are optional.

-s : Specify subject;if subject contains spaces, use quotes.

-c : Send carbon copies to list of users seperated by comma.

-b : Send blind carbon copies to list of users seperated by comma.

Hope my answer clarifies your doubt.

this accepts text, how can you end the mail body? – knocte Mar 11 '16 at 8:08

7楼
+30
echo "Sending emails ..."
NOW=$(date +"%F %H:%M")
echo $NOW  " Running service" >> open_files.log
header=`echo "Service Restarting: " $NOW`


mail -s "$header" abc.xyz@google.com,   
              cde.mno@yahoo.com,  < open_files.log
8楼
+10

Customizing FROM address

MESSAGE="SOME MESSAGE"
SUBJECT="SOME SUBJECT"
TOADDR="u@u.com"
FROM="DONOTREPLY"

echo $MESSAGE | mail  -s "$SUBJECT" $TOADDR  -- -f $FROM

An except from man mail: -f [file] Read in the contents of the user's mbox (or the specified file) for processing; when mailx is quit, it writes undeleted messages back to this file. The string file is handled as described for the folder command below. – ZJ Lyu May 13 '17 at 3:48

9楼
0

If you want to send more than two person or DL :

echo "Message Body" | mailx -s "Message Title" -r sender@someone.com receiver1@someone.com,receiver_dl@.com

here:

  • -s = subject or mail title
  • -r = sender mail or DL
10楼
0

Here is a multifunctional function to tackle mail sending with several attachments:

enviaremail() {
values=$(echo "$@" | tr -d '
')
listargs=()
listargs+=($values)
heirloom-mailx $( attachment=""
for (( a = 5; a < ${#listargs[@]}; a++ )); do
attachment=$(echo "-a ${listargs[a]} ")
echo "${attachment}"
done) -v -s "${titulo}" 
-S smtp-use-starttls 
-S ssl-verify=ignore 
-S smtp-auth=login 
-S smtp=smtp://$1 
-S from="${2}" 
-S smtp-auth-user=$3 
-S smtp-auth-password=$4 
-S ssl-verify=ignore 
$5 < ${cuerpo}
}

function call: enviaremail "smtp.mailserver:port" "from_address" "authuser" "'pass'" "destination" "list of attachments separated by space"

Note: Remove the double quotes in the call

In addition please remember to define externally the $titulo (subject) and $cuerpo (body) of the email prior to using the function

0
votes
answers
71 views
+10

使用nano中的當前時間戳創建新的列在Hive中

1

我想在nano表中使用當前時間戳在nano秒中創建一列。我怎麼能在插入數據時做到這一點?使用nano中的當前時間戳創建新的列在Hive中

沙发
0
1

當前current_timestamp功能蜂巢,不納秒時間。

但是你總是可以用create your own functions使用配置單元UDF來處理nano秒時間戳。

下面是UDF的示例,它將返回納秒時間的值long

import org.apache.hadoop.hive.ql.exec.UDF; 
import org.apache.hadoop.hive.ql.udf.UDFType; 

@UDFType(stateful = true) 
public class NanoTimeUdf extends UDF{ 

    public long evaluate() { 
     return System.nanoTime(); 
    } 

} 

創建一個JAR出上面的Java代碼(說tonanotime.jar),並添加JAR創建UDF函數返回當前時間納米。

ADD JAR /home/amit/tonanotime.jar; 
create TEMPORARY FUNCTION toNanoTime AS 'NanoTimeUdf'; 

現在,toNanoTime功能可用,您可以選擇查詢使用插入到新表,如: e.g

insert into table select toNanoTime() from other_table; 
228
votes
answers
23 views
+10

check if directory exists and delete in one command unix

Is it possible to check if a directory exists and delete if it does,in Unix using a single command? I have situation where I use ANT 'sshexec' task where I can run only a single command in the remote machine. And I need to check if directory exists and delete it...

up vote 104 down vote accepted favorite
沙发
+1040
+50

Assuming $WORKING_DIR is set to the directory... this one-liner should do it:

if [ -d "$WORKING_DIR" ]; then rm -Rf $WORKING_DIR; fi

(otherwise just replace with your directory)

+970

Why not just use rm -rf /some/dir? That will remove the directory if it's present, otherwise do nothing.

我認為原始問題的意思是:僅在文件/目錄存在或不存在時執行刪除。此命令可以工作並產生類似的結果,但實際上在命令更有意義之前進行測試。 - Ankur Chauhan 2013年1月28日5:04

@AnkurChauhan +1,如果dir不存在,我會收到警告。 - inf3rno 2015年8月25日7:15

這不是問題的答案。 - Marcelo Filho於19年1月19日8:26

如上所述,這不是答案。在刪除之前進行測試的一個原因是在Jenkins工作中。如果該目錄不存在並且您嘗試將其刪除,則該作業將失敗。事先檢查是更好的選擇。 - 17年3月1日21:38伐木工人

請你做你的部分,然後向下看這個答案。 - 2017年10月11日在19:43的雙重情節

+270

Try:

bash -c '[ -d my_mystery_dirname ] && run_this_command'

EDIT: This will work if you can run bash on the remote machine....

EDIT 2: In bash, [ -d something ] checks if there is directory called 'something', returning a success code if it exists and is a directory. Chaining commands with && runs the second command only if the first one succeeded. So [ -d somedir ] && command runs the command only if the directory exists.

是的,並在shell中嘗試它以確保它是你想要的。 - sinelaw 2011年1月30日22:37

首先,我想看看它是否在本地工作,我可以遠程發送命令。我在看是說要檢查“/ test”目錄..只是[-d / test] && mkdir / test? - remo 2011年1月30日22:41

是的,試試吧! - sinelaw 2011年1月30日22:42

它沒有錯誤,也沒有創建目錄 - remo 2011年1月30日22:42

如果存在則應該給出錯誤,因為它檢查目錄是否存在,如果是,則嘗試創建它。這就是它對我的影響(剛剛測試過)。它不應該創建目錄。既然你想刪除你可能需要:[ - d / test] && rmdir / test(或rm -rf / test如果它不是空的你想要刪除所有的內容) - sinelaw 1月30日'11在22:44

175
votes
answers
32 views
+10

Unix - copy contents of one directory to another [closed]

Folder1/
    -fileA.txt
    -fileB.txt
    -fileC.txt

> mkdir Folder2/

> [copy command]

And now Folder2/ looks like:

Folder2/
    -fileA.txt
    -fileB.txt
    -fileC.txt   

How to make this happen? I have tried cp -r Folder1/ Folder2/ but I ended up with:

Folder2/
    Folder1/
        -fileA.txt
        -fileB.txt
        -fileC.txt

Which is close but not exactly what I wanted.

Thanks!

up vote 112 down vote accepted favorite
沙发
+1120
+50

Try this:

cp Folder1/* Folder2/

但這不會復制隱藏文件,對吧? - Fabrizio Regini 2014年1月29日10:35

正確。cp -R會,但是會遞歸複製,所以你可能會也可能不想使用它。你可以做cp Folder1 /.* Folder2 /只複製隱藏文件。 - 傑夫於2014年1月30日0:12

請注意,如果您使用“sudo”或等效文件並且該目錄包含大量文件,則會失敗。我得到sudo:無法執行/ bin / cp:參數列表太長 - Nathan Osman 2014年9月15日19:49

注意SCP的語法略有不同,請看這裡:stackoverflow.com/a/26346339/1984636 - sivi 2016年2月4日8:35

+410

Quite simple, with a * wildcard.

cp -r Folder1/* Folder2/

But according to your example recursion is not needed so the following will suffice:

cp Folder1/* Folder2/

EDIT:

Or skip the mkdir Folder2 part and just run:

cp -r Folder1 Folder2

如果ls | sort -k1.5 | 頭-8> folder1那怎麼辦呢? - Pooja 2013年4月4日4:49

+220

To make an exact copy, permissions, ownership, and all use "-a" with "cp". "-r" will copy the contents of the files but not necessarily keep other things the same.

cp -av Source/* Dest/

(make sure Dest/ exists first)

If you want to repeatedly update from one to the other or make sure you also copy all dotfiles, rsync is a great help:

rsync -av --delete Source/ Dest/

This is also "recoverable" in that you can restart it if you abort it while copying. I like "-v" because it lets you watch what is going on but you can omit it.

什麼是-v? - HattrickNZ 2014年6月23日0:53

詳細,只是為了看看發生了什麼。 - 布萊恩懷特2014年6月23日1:05

如果你有很多文件,-v選項可能需要一些時間。如果您不需要輸出,請僅使用-a。 - Eyal Levin 2016年11月24日13:32

您必須在相當慢的網絡連接上運行才能通過文本輸出減慢副本速度。即便如此,它可能只是返回命令提示符,而不是副本本身。 - Brian White於2016年11月26日凌晨2點10分

95
votes
answers
61 views
+10

How to convert DOS/Windows newline (CRLF) to Unix newline (LF) in a Bash script?

How can I programmatically (i.e., not using vi) convert DOS/Windows newlines to Unix?

The dos2unix and unix2dos commands are not available on certain systems. How can I emulate these with commands like sed/awk/tr?

沙发
+190

This problem can be solved with standard tools, but there are sufficiently many traps for the unwary that I recommend you install the flip command, which was written over 20 years ago by Rahul Dhesi, the author of zoo. It does an excellent job converting file formats while, for example, avoiding the inadvertant destruction of binary files, which is a little too easy if you just race around altering every CRLF you see...

有沒有辦法以流式方式做到這一點,而無需修改原始文件? - augurar 2013年12月7日22:08

@augurar你可以檢查“類似包”package.debian.org/wheezy/flip - n611x007 2014年8月19日11:12

我只是通過運行帶有錯誤標誌的texxto來體驗破壞我操作系統的一半。如果要在整個文件夾中執行此操作,請特別小心。 - A_P 9月13日'18在13:21

板凳
+140

The solutions posted so far only deal with part of the problem, converting DOS/Windows' CRLF into Unix's LF; the part they're missing is that DOS use CRLF as a line separator, while Unix uses LF as a line terminator. The difference is that a DOS file (usually) won't have anything after the last line in the file, while Unix will. To do the conversion properly, you need to add that final LF (unless the file is zero-length, i.e. has no lines in it at all). My favorite incantation for this (with a little added logic to handle Mac-style CR-separated files, and not molest files that're already in unix format) is a bit of perl:

perl -pe 'if ( s/
?/
/g ) { $f=1 }; if ( $f || ! $m ) { s/([^
])z/$1
/ }; $m=1' PCfile.txt

Note that this sends the Unixified version of the file to stdout. If you want to replace the file with a Unixified version, add perl's -i flag.

RIP我的數據文件。xD - Ludovic Zenohate Lagouardette在2016年1月21日10點53分出了問題

@LudovicZenohateLagouardette它是純文本文件(即csv或tab-demited文本),還是其他什麼?如果它是某種數據庫格式,那麼將其操作就好像它是文本一樣,很可能會破壞其內部結構。 - 戈登戴維森2016年1月23日20:53

一個純文本csv,但我認為這很奇怪。我認為因此而搞砸了。不過不用擔心。我總是收集備份,這甚至不是真正的數據集,只有1gb。真實的是26gb。 - Ludovic Zenohate Lagouardette 2016年1月24日8:02

地板
+130

If you don't have access to dos2unix, but can read this page, then you can copy/paste dos2unix.py from here.

#!/usr/bin/env python
"""
convert dos linefeeds (crlf) to unix (lf)
usage: dos2unix.py <input> <output>
"""
import sys

if len(sys.argv[1:]) != 2:
  sys.exit(__doc__)

content = ''
outsize = 0
with open(sys.argv[1], 'rb') as infile:
  content = infile.read()
with open(sys.argv[2], 'wb') as output:
  for line in content.splitlines():
    outsize += len(line) + 1
    output.write(line + '
')

print("Done. Saved %s bytes." % (len(content)-outsize))

Cross-posted from superuser.

用法具有誤導性。真正的dos2unix默認轉換所有輸入文件。您的用法意味著-n參數。真正的dos2unix是一個從stdin讀取的過濾器,如果沒有給出文件,則寫入stdout。 - jfs 2015年7月6日11:32

4楼
+120

You can use vim programmatically with the option -c {command} :

Dos to Unix:

vim file.txt -c "set ff=unix" -c ":wq"

Unix to dos:

vim file.txt -c "set ff=dos" -c ":wq"

"set ff=unix/dos" means change fileformat (ff) of the file to Unix/DOS end of line format

":wq" means write file to disk and quit the editor (allowing to use the command in a loop)

這似乎是最優雅的解決方案,但缺乏對wq意味著什麼的解釋是不幸的。 - Jorrick Sleijster 2月23日12:23

任何使用vi的人都會知道:wq的含義。對於那些沒有3個字符的人意味著1)打開vi命令區域,2)寫入和3)退出。 - David Newcomb 2月27日10:24

我不知道你可以從CLI交互式地向vim添加命令--Robert Dundon 4月4日13:24

5楼
+80

Super duper easy with PCRE;

As a script, or replace $@ with your files.

#!/usr/bin/env bash
perl -pi -e 's/
/
/g' -- $@

This will overwrite your files in place!

I recommend only doing this with a backup (version control or otherwise)

謝謝!這有效,雖然我正在寫文件名而沒有 - 。我選擇這個解決方案是因為它易於理解並適應我。僅供參考,這是開關的作用:-p假設“while input”循環,-i編輯輸入文件,-e執行以下命令 - Rolf 10年11月11日在12:21

嚴格地說,PCRE是Perl的正則表達式引擎的重新實現,而不是Perl的正則表達式引擎。他們都有這種能力,雖然也有不同之處,儘管這個名字很有意義。 - 2017年10月27日8:24

6楼
+80

To convert a file in place do

dos2unix <filename>

To output converted text to a different file do

dos2unix -n <input-file> <output-file>

It's already installed on Ubuntu and is available on homebrew with brew install dos2unix


I know the question explicitly asks for alternatives to this utility but this is the first google search result for "convert dos to unix line endings".

7楼
+60

An even simpler awk solution w/o a program:

awk -v ORS='
' '1' unix.txt > dos.txt

Technically '1' is your program, b/c awk requires one when given option.

UPDATE: After revisiting this page for the first time in a long time I realized that no one has yet posted an internal solution, so here is one:

while IFS= read -r line;
do printf '%s
' "${line%$'
'}";
done < dos.txt > unix.txt

這很方便,但只是要清楚:這會轉換Unix - > Windows / DOS,這與OP要求的方向相反。 - mklement0 2015年2月28日6:01

這是故意的,留給作者練習。eyerolls awk -v RS ='''1'dos.txt> unix.txt - nawK 2015年3月1日4:14

偉大(並為你的教育技巧稱讚)。 - mklement0 2015年3月1日4:35

“b / c awk在給定選項時需要一個。” - awk總是需要一個程序,無論是否指定了選項。 - mklement0 2015年3月1日4:37

純粹的bash解決方案很有趣,但比同等的awk或sed解決方案慢得多。此外,您必須使用IFS = read -r行來忠實地保留輸入行,否則會修剪前導和尾隨空格(或者,在read命令中不使用變量名並使用$ REPLY)。 - mklement0 2015年3月1日6:14

8楼
+40

interestingly in my git-bash on windows sed "" did the trick already:

$ echo -e "abc
" >tst.txt
$ file tst.txt
tst.txt: ASCII text, with CRLF line terminators
$ sed -i "" tst.txt
$ file tst.txt
tst.txt: ASCII text

My guess is that sed ignores them when reading lines from input and always writes unix line endings on output.

9楼
+30

This worked for me

tr "
" "
" < sampledata.csv > sampledata2.csv 

這會將每個DOS換行符轉換為兩個UNIX換行符。 - Melebius 2015年8月4日6:11

10楼
+30

Had just to ponder that same question (on Windows-side, but equally applicable to linux.) Suprisingly nobody mentioned a very much automated way of doing CRLF<->LF conversion for text-files using good old zip -ll option (Info-ZIP):

zip -ll textfiles-lf.zip files-with-crlf-eol.*
unzip textfiles-lf.zip 

NOTE: this would create a zip file preserving the original file names but converting the line endings to LF. Then unzip would extract the files as zip'ed, that is with their original names (but with LF-endings), thus prompting to overwrite the local original files if any.

Relevant excerpt from the zip --help:

zip --help
...
-l   convert LF to CR LF (-ll CR LF to LF)
11楼
+20

TIMTOWTDI!

perl -pe 's/
/
/; s/([^
])z/$1
/ if eof' PCfile.txt

Based on @GordonDavisson

One must consider the possibility of [noeol] ...

12楼
+10

For Mac osx if you have homebrew installed [http://brew.sh/][1]

brew install dos2unix

for csv in *.csv; do dos2unix -c mac ${csv}; done;

Make sure you have made copies of the files, as this command will modify the files in place. The -c mac option makes the switch to be compatible with osx.

dos2unix結果非常方便! - HelloGoodbye 2014年8月21日15:12

這個答案真的不是原始海報的問題。 - hlin117 2015年2月7日17:43

OS X用戶不應該使用-c mac,它用於轉換僅基於OS X CR的新行。您只想將該模式用於Mac OS 9或之前的文件。 - askewchan 2016年4月14日13:20

13楼
+10

You can use awk. Set the record separator (RS) to a regexp that matches all possible newline character, or characters. And set the output record separator (ORS) to the unix-style newline character.

awk 'BEGIN{RS="
|
|
|

";ORS="
"}{print}' windows_or_macos.txt > unix.txt

這是對我有用的那個(MacOS,git diff顯示^ M,在vim中編輯) - Dorian Mar 1 '17 at 9:17

14楼
+10

On Linux it's easy to convert ^M (ctrl-M) to *nix newlines (^J) with sed.

It will something like this on the CLI, there will actually be a line break in the text. However, the passes that ^J along to sed:

sed 's/^M/
/g' < ffmpeg.log > new.log

You get this by using ^V (ctrl-V), ^M (ctrl-M) and (backslash) as you type:

sed 's/^V^M/^V^J/g' < ffmpeg.log > new.log
15楼
0

As an extension to Jonathan Leffler's Unix to DOS solution, to safely convert to DOS when you're unsure of the file's current line endings:

sed '/^M$/! s/$/^M/'

This checks that the line does not already end in CRLF before converting to CRLF.

16楼
0
sed --expression='s/
/
/g'

Since the question mentions sed, this is the most straight forward way to use sed to achieve this. What the expression says is replace all carriage-return and line-feed with just line-feed only. That is what you need when you go from Windows to Unix. I verified it works.

嘿約翰保羅 - 這個答案被標記為刪除,所以出現在我的審查隊列中。一般來說,當你有一個8歲的問題,有22個答案時,你會想要解釋你的答案是如何有用的,而其他現有的答案卻沒有。 - zzxyz 18年8月18日22:34

17楼
0

I made a script based on the accepted answer so you can convert it directly without needing an additional file in the end and removing and renaming afterwards.

convert-crlf-to-lf() {
    file="$1"
    tr -d '15' <"$file" >"$file"2
    rm -rf "$file"
    mv "$file"2 "$file"
}

just make sure if you have a file like "file1.txt" that "file1.txt2" doesn't already exist or it will be overwritten, I use this as a temporary place to store the file in.

18楼
-30

I tried sed 's/^M$//' file.txt on OSX as well as several other methods (http://www.thingy-ma-jig.co.uk/blog/25-11-2010/fixing-dos-line-endings or http://hintsforums.macworld.com/archive/index.php/t-125.html). None worked, the file remained unchanged (btw Ctrl-v Enter was needed to reproduce ^M). In the end I used TextWrangler. Its not strictly command line but it works and it doesn't complain.

19楼
-50

There are plenty of awk/sed/etc answers so as a supplement (since this is one of the top search results for this issue):

You may not have dos2unix but do you have iconv?

iconv -f UTF-16LE -t UTF-8 [filename.txt]
-f from format type
-t to format type

Or all files in a directory:

find . -name "*.sql" -exec iconv -f UTF-16LE -t UTF-8 {} -o ./{} ;

This runs the same command, on all .sql files in the current folder. -o is the output directory so you can have it replace the current files, or, for safety/backup reasons, output to a separate directory.

這嘗試實現從UTF-16LE到UTF-8的編碼轉換,但它不接觸行結尾。它與被問到的問題無關。 - Palec 2010年10月13日13:36

我的錯。我將驗證這一點,但是,我剛剛使用THAT DAY修復了我的文件沒有運行grep的問題,因為它們是Windows格式化的。 - Katastic Voyage 17年10月14日17:34

這也是一個常見的問題,但不是OP所要求的問題(並且比CRLF問題少得多)。 - 2017年10月27日8:22

107
votes
answers
16 views
+10

Change the “From:” address in Unix “mail”

Sending a message from the Unix command line using mail TO_ADDR results in an email from $USER@$HOSTNAME. Is there a way to change the "From:" address inserted by mail?

For the record, I'm using GNU Mailutils 1.1/1.2 on Ubuntu (but I've seen the same behavior with Fedora and RHEL).

[EDIT]

$ mail -s Testing chris@example.org                                                                  
Cc: 
From: foo@bar.org

Testing
.

yields

Subject: Testing
To: <chris@example.org>
X-Mailer: mail (GNU Mailutils 1.1)
Message-Id: <E1KdTJj-00025z-RK@localhost>
From: <chris@localhost>
Date: Wed, 10 Sep 2008 13:17:23 -0400

From: foo@bar.org

Testing

The "From: foo@bar.org" line is part of the message body, not part of the header.

沙发
+420

On Centos 5.3 I'm able to do:

mail -s "Subject" user@address.com -- -f from@address.com < body

The double dash stops mail from parsing the -f argument and passes it along to sendmail itself.

你可以幫忙,這曾經工作 - 但是,自從最近的更新,它不再有效,它只是嘗試另外發送電子郵件到-f @ hostname - Wil 1月15日15:22

不適用於CentOS 6.3。使用-r標誌嘗試@ ubuntu-fanboy的答案。 - bejota 2015年5月13日21:22

板凳
+210

GNU mailutils's 'mail' command doesn't let you do this (easily at least). But If you install 'heirloom-mailx', its mail command (mailx) has the '-r' option to override the default '$USER@$HOSTNAME' from field.

echo "Hello there" | mail -s "testing" -r sender@company.com recipient@company.com

Works for 'mailx' but not 'mail'.

$ ls -l /usr/bin/mail
lrwxrwxrwx 1 root root 22 2010-12-23 08:33 /usr/bin/mail -> /etc/alternatives/mail
$ ls -l /etc/alternatives/mail
lrwxrwxrwx 1 root root 23 2010-12-23 08:33 /etc/alternatives/mail -> /usr/bin/heirloom-mailx

也許知道在Ubuntu 14.04 LTS上預裝的GNU mailutils郵件命令支持-r選項可能會有用,這樣您就可以輕鬆設置發件人地址。 - gerlos 2015年6月4日17:17

地板
+130
mail -s "$(echo -e "This is the subject
From: Paula <johny@paula.com>

Reply-to: 1232564@yourserver.com
Content-Type: text/html
")" 
milas.josh@gmail.com < htmlFileMessage.txt

the above is my solution....any extra headers can be added just after the from and before the reply to...just make sure you know your headers syntax before adding them....this worked perfectly for me.

這對我的例子很有幫助.. 當我更改電子郵件地址主題等所有標題都是顯示的? - bsmoo 2013年7月11日16:11

這對我在netBSD中工作 - jedi 2014年9月24日11:58

在Mac OS(優勝美地)上沒有工作 - xaphod 2015年2月4日14:22

4楼
+70

Plus it's good to use -F option to specify Name of sender.

Something like this:

mail -s "$SUBJECT" $MAILTO -- -F $MAILFROM -f ${MAILFROM}@somedomain.com

Or just look at available options: http://www.courier-mta.org/sendmail.html

5楼
+50

It's also possible to set both the From name and from address using something like:

 echo test | mail -s "test" example@example.com -- -F'Some Name<example2@example.com>' -t

For some reason passing -F'Some Name' and -fexample2@example.com doesn't work, but passing in the -t to sendmail works and is "easy".

6楼
+40

Here are some options:

  • If you have privelige enough, configure sendmail to do rewrites with the generics table

  • Write the entire header yourself (or mail it to yourself, save the entire message with all headers, and re-edit, and send it with rmail from the command line

  • Send directly with sendmail, use the "-f" command line flag and don't include your "From:" line in your message

These aren't all exactly the same, but I'll leave it to you look into it further.

On my portable, I have sendmail authenticating as a client to an outgoing mail server and I use generics to make returning mail come to another account. It works like a charm. I aggregate incoming mail with fetchmail.

7楼
+30

I don't know if it's the same with other OS, but in OpenBSD, the mail command has this syntax:

mail to-addr ... -sendmail-options ...

sendmail has -f option where you indicate the email address for the FROM: field. The following command works for me.

mail recepient@example.com -f from@example.com

適合我!(也是openBSD - Macbook pro。) - Aeonaut 3月28日在2:23

更新 - 電子郵件收件人仍然可以在from@example.com之前查看我的本地帳戶名稱 - 例如,“Aeonaut from@example.com”。知道怎麼改變這個嗎? - Aeonaut 3月28日在2:35

對我不起作用。(os x mountain lion)“郵件:不能給-f和人們發送。” - 強尼於2013年5月27日5:54

8楼
+30

Thanks BEAU

mail -s "Subject" user@address.com -- -f from@address.com

I just found this and it works for me. The man pages for mail 8.1 on CentOS 5 doesn't mention this. For -f option, the man page says:

-f Read messages from the file named by the file operand instead of the system mailbox. (See also folder.) If no file operand is specified, read messages from mbox instead of the system mailbox.

So anyway this is great to find, thanks.

-f當然不是你的例子中的郵件選項,因為它前面是 - 。該選項將於2016年2月8日6:55交給您的MTA - Anthon

9楼
+30

I derived this from all the above answers. Nothing worked for me when I tried each one of them. I did lot of trail and error by combining all the above answers and concluded on this. I am not sure if this works for you but it worked for me on Ununtu 12.04 and RHEL 5.4.

echo "This is the body of the mail" | mail -s 'This is the subject' '<receiver-id1@email.com>,<receiver-id2@email.com>' -- -F '<SenderName>' -f '<from-id@email.com>'

One can send the mail to any number of people by adding any number of receiver id's and the mail is sent by SenderName from from-id@email.com

Hope this helps.

10楼
+30

On Debian 7 I was still unable to correctly set the sender address using answers from this question, (would always be the hostname of the server) but resolved it this way.

Install heirloom-mailx

apt-get install heirloom-mailx

ensure it's the default.

update-alternatives --config mailx

Compose a message.

mail -s "Testing from & replyto" -r "sender <sender@example.com>" -S replyto="sender@example.com" recipient@example.net < <(echo "Test message")

這個update-alternatives命令在做什麼?它給了我3個選擇,我很困惑。 - Stephane 17年7月3日在13:54

11楼
+20

echo "body" | mail -S from=address@foo.com "Hello"

-S lets you specify lots of string options, by far the easiest way to modify headers and such.

12楼
+10

On CentOS this worked for me:

echo "email body" | mail -s "Subject here" -r from_email_address email_address_to
13楼
0

this worked for me

echo "hi root"|mail -rsawrub@testingdomain.org -s'testinggg' root
14楼
0

On CentOS 5.5, the easiest way I've found to set the default from domain is to modify the hosts file. If your hosts file contains your WAN/public IP address, simply modify the first hostname listed for it. For example, your hosts file may look like:

...
11.22.33.44 localhost default-domain whatever-else.com
...

To make it send from whatever-else.com, simply modify it so that whatever-else.com is listed first, for example:

...
11.22.33.44 whatever-else.com localhost default-domain
...

I can't speak for any other distro (or even version of CentOS) but in my particular case, the above works perfectly.

15楼
0

echo "test" | mailx -r fake@example.com -s 'test' email@example.com

It works in OpenBSD.

16楼
0

What allowed me to have a custom reply-to address on an Ubuntu 16.04 with UTF-8 encoding and a file attachment:

Install the mail client:

sudo apt-get install heirloom-mailx

Edit the SMTP configuration:

sudo vim /etc/ssmtp/ssmtp.conf
mailhub=smtp.gmail.com:587
FromLineOverride=YES
AuthUser=???@gmail.com
AuthPass=???
UseSTARTTLS=YES

Send the mail:

sender='send@domain.com'
recipient='recipient@domain.com'
zipfile="results/file.zip"
today=`date +\%d-\%m-\%Y`
mailSubject='My subject on the '$today
read -r -d '' mailBody << EOM
Find attached the zip file.

Regards,
EOM
mail -s "$mailSubject" -r "Name <$sender>" -S replyto="$sender" -a $zipfile $recipient < <(echo $mailBody)
17楼
0

None of the above solutions are working for me...

#!/bin/bash

# Message
echo "My message" > message.txt

# Mail
subject="Test"
mail_header="From: John Smith <john.smith@example.com>"
recipients="recipient@example.com"

#######################################################################
cat message.txt | mail -s "$subject" -a "$mail_header" -t "$recipients"
18楼
-10

The answers provided before didn't work for me on CentOS5. I installed mutt. It has a lot of options. With mutt you do this this way:

export EMAIL=myfrom@example.com
export REPLYTO=myreplyto@example.com
mutt -s Testing chris@example.org
246
votes
answers
36 views
+10

How do I get the find command to print out the file size with the file name?

If I issue the find command as follows:

$ find . -name *.ear

It prints out:

./dir1/dir2/earFile1.ear
./dir1/dir2/earFile2.ear
./dir1/dir3/earFile1.ear

What I want to 'print' to the command line is the name and the size:

./dir1/dir2/earFile1.ear  5000 KB
./dir1/dir2/earFile2.ear  5400 KB
./dir1/dir3/earFile1.ear  5400 KB
up vote 90 down vote accepted favorite
沙发
+900
+50
find . -name '*.ear' -exec ls -lh {} ;

just the h extra from jer.drab.org's reply. saves time converting to MB mentally ;)

This version will exec "ls" process for each file. If you have many files (say, over a thousand) you better optimize that by either: find . -name '*.ear' -exec ls -lh {} + ; (GNU extension) or find . -name '*.ear' -print0 | xargs -0 ls -lh. Also you may like to add -type f if you're only interested in files (or add -d to ls if you want directories themselves included without their contents). – ash108 Mar 24 '12 at 14:36

Your answer does not exclude directories, so you will end up running ls on directories as well, which clearly is not the right thing to do. – Faheem Mitha Nov 7 '14 at 10:08

This is a really inefficient way of doing things - and unfortunately ash108's comments are also not ideal. It's much better to use the -printf option to find. – oskarpearson May 19 '15 at 9:47

dmazzoni's answer is much more efficient because it avoids a fork+exec on every file (100x faster on my machine). I had to change %k to %s. @FaheemMitha: you can add options like -type f to only count regular files. – Mr Fooz Sep 29 '16 at 23:19

@FaheemMitha easy solution is find . -name "*.ear" -type f for files. -type d works for directories. – anon58192932 Oct 20 '16 at 17:38

+920

You need to use -exec or -printf. Printf works like this:

find . -name *.ear -printf "%p %k KB
"

-exec is more powerful and lets you execute arbitrary commands - so you could use a version of 'ls' or 'wc' to print out the filename along with other information. 'man find' will show you the available arguments to printf, which can do a lot more than just filesize.

[edit] -printf is not in the official POSIX standard, so check if it is supported on your version. However, most modern systems will use GNU find or a similarly extended version, so there is a good chance it will be implemented.

It looks like your example is more precise, but I can't seem to get the example to work on Solaris 10. – Brian Sep 15 '08 at 17:21

I'm afraid Solaris find does not support -printf at all: jrwren.wrenfam.com/blog/2006/10/07/solaris-find-sucks cs.bgu.ac.il/~arik/usail/man/solaris/find.1.html You could install GNU find if you can be bothered, otherwise you need to use exec or | as suggested by others. – Leigh Caldwell Sep 15 '08 at 17:27

+1 This seems more cleaner. To format the output I would prefer add column command. find . -name *.ear -printf "%p %k KB " | column -t – Amol Dec 6 '12 at 9:53

This answer is a much more correct way to do it than the accepted answer – menacingly Apr 26 '15 at 14:12

The -printf option doesn't work in OS X either. See stackoverflow.com/questions/752818/…. – abeboparebop Jun 7 '16 at 9:02

+210

a simple solution is to use the -ls option in find:

find . -name *.ear -ls

That gives you each entry in the normal "ls -l" format. Or, to get the specific output you seem to be looking for, this:

find . -name *.ear -printf "%p	%k KB
"

Which will give you the filename followed by the size in KB.

+190

Using gnu find, I think this is what you want. It finds all real files and not directories (-type f), and for each one prints the filename (%p), a tab ( ), the size in kilobytes (%k), the suffix " KB", and then a newline ( ).

find . -type f -printf '%p	%k KB
'

If the printf command doesn't format things the way you want, you can use exec, followed by the command you want to execute on each file. Use {} for the filename, and terminate the command with a semicolon (;). On most shells, all three of those characters should be escaped with a backslash.

Here's a simple solution that finds and prints them out using "ls -lh", which will show you the size in human-readable form (k for kilobytes, M for megabytes):

find . -type f -exec ls -lh {} ;

As yet another alternative, "wc -c" will print the number of characters (bytes) in the file:

find . -type f -exec wc -c {} ;

+1 Except for having omitted -name '*.ear' in the examples, this should be the #1 answer. It answers the question precisely, explains the meaning of the syntax, and gives multiple alternatives. – OsakaWebbie Jul 28 '17 at 6:40

this should be accepted answer as it doesn't run new command for every line, thus it is much more faster than the currently accepted one from shyam – jirislav Feb 5 '18 at 23:02

Indeed this should be the right answer. Also, find . -type f -printf '%k KB %p ' i.e., printing the file size first, will most likely be a nicer output in terms of formatting due to the alignment. – myradio Jun 25 '18 at 12:41

+70
find . -name '*.ear' -exec du -h {} ;

This gives you the filesize only, instead of all the unnecessary stuff.

如果您需要以字節為單位的大小,wc可以更好地工作。 - 亞歷克斯17年7月18日在13:32

+40

Awk can fix up the output to give just what the questioner asked for. On my Solaris 10 system, find -ls prints size in KB as the second field, so:

% find . -name '*.ear' -ls | awk '{print $2, $11}'
5400 ./dir1/dir2/earFile2.ear
5400 ./dir1/dir2/earFile3.ear
5400 ./dir1/dir2/earFile1.ear

Otherwise, use -exec ls -lh and pick out the size field from the output. Again on Solaris 10:

% find . -name '*.ear' -exec ls -lh {} ; | awk '{print $5, $9}'
5.3M ./dir1/dir2/earFile2.ear
5.3M ./dir1/dir2/earFile3.ear
5.3M ./dir1/dir2/earFile1.ear
+40

Why not use du -a ? E.g.

find . -name "*.ear" -exec du -a {} ;

Works on a Mac

+30

I struggled with this on Mac OS X where the find command doesn't support -printf.

A solution that I found, that admittedly relies on the 'group' for all files being 'staff' was...

ls -l -R | sed 's/(.*)staff *([0-9]*)..............(.*)/2 3/'

This splits the ls long output into three tokens

  1. the stuff before the text 'staff'
  2. the file size
  3. the file name

And then outputs tokens 2 and 3, i.e. output is number of bytes and then filename

8071 sections.php
54681 services.php
37961 style.css
13260 thumb.php
70951 workshops.php

有用的知道..找到的GNU版本可以通過自製的brew install findutils - mwfearnley 5月21日9:56

+20

This should get you what you're looking for, formatting included (i.e. file name first and size afterward):

find . -type f -iname "*.ear" -exec du -ah {} ; | awk '{print $2"	", $1}'

sample output (where I used -iname "*.php" to get some result):

./plugins/bat/class.bat.inc.php  20K
./plugins/quotas/class.quotas.inc.php    8.0K
./plugins/dmraid/class.dmraid.inc.php    8.0K
./plugins/updatenotifier/class.updatenotifier.inc.php    4.0K
./index.php      4.0K
./config.php     12K
./includes/mb/class.hwsensors.inc.php    8.0K
+10

You could try this:

find. -name *.ear -exec du {} ;

This will give you the size in bytes. But the du command also accepts the parameters -k for KB and -m for MB. It will give you an output like

5000  ./dir1/dir2/earFile1.ear
5400  ./dir1/dir2/earFile2.ear
5400  ./dir1/dir3/earFile1.ear
+10
find . -name "*.ear" | xargs ls -sh
+10
$ find . -name "test*" -exec du -sh {} ;
4.0K    ./test1
0       ./test2
0       ./test3
0       ./test4
$

Scripter World reference

+10

Try the following commands:

GNU stat:

find . -type f -name *.ear -exec stat -c "%n %s" {} ';'

BSD stat:

find . -type f -name *.ear -exec stat -f "%N %z" {} ';'

however stat isn't standard, so du or wc could be a better approach:

find . -type f -name *.ear -exec sh -c 'echo "{} $(wc -c < {})"' ';'
0
find . -name "*.ear" -exec ls -l {} ;
383
votes
answers
39 views
+10

How to get a unix script to run every 15 seconds?

I've seen a few solutions, including watch and simply running a looping (and sleeping) script in the background, but nothing has been ideal.

I have a script that needs to run every 15 seconds, and since cron won't support seconds, I'm left to figuring out something else.

What's the most robust and efficient way to run a script every 15 seconds on unix? The script needs to also run after a reboot.

up vote 75 down vote accepted favorite
沙发
+750
+50

I would use cron to run a script every minute, and make that script run your script four times with a 15-second sleep between runs.

(That assumes your script is quick to run - you could adjust the sleep times if not.)

That way, you get all the benefits of cron as well as your 15 second run period.

Edit: See also @bmb's comment below.

搖晃拳頭呵呵 - 艾登貝爾2009年6月23日18:20

@Aiden:哈!我的剋星,我們又見面了! - RichieHindle 09年6月23日18:21

如果腳本運行所需的時間不一致,請製作腳本的四個副本。一個人在開始前睡15秒,另外30個,另一個45,另一個零。然後每分鐘運行四個。 - bmb 09年6月23日18:23

@RichieHindle - Have no fear, I got assassinated for not granulating the minutes into seconds. But i'm watching you :P – Aiden Bell Jun 23 '09 at 18:23

How can this be cron is triggered every 1 minute – DevZer0 Jun 25 '13 at 7:11

+2800

If you insist of running your script from cron:

* * * * * /foo/bar/your_script
* * * * * sleep 15; /foo/bar/your_script
* * * * * sleep 30; /foo/bar/your_script
* * * * * sleep 45; /foo/bar/your_script

and replace your script name&path to /foo/bar/your_script

this is very clever. +1 – SingleNegationElimination Jun 23 '09 at 18:38

This worked perfectly for me. The soltion above this on using a background task was spawning several child processes and causing memory issues on my end. – Hacknightly Jun 7 '12 at 15:06

if running php script do this:* * * * * sleep 15; php /foo/bar/your_script – ImaginedDesign Mar 7 '14 at 17:15

if running php script you can prepend the line #!/usr/bin/php to the top of your php script and make it executable – Aaron Blenkush Sep 8 '14 at 18:23

I feel embarrassed that I had to google for this solution. Maybe stackoverflow makes me think less. – chris finne Oct 17 '14 at 16:10

+140

Modified version of the above:

mkdir /etc/cron.15sec
mkdir /etc/cron.minute
mkdir /etc/cron.5minute

add to /etc/crontab:

* * * * * root run-parts /etc/cron.15sec > /dev/null 2> /dev/null
* * * * * root sleep 15; run-parts /etc/cron.15sec > /dev/null 2> /dev/null
* * * * * root sleep 30; run-parts /etc/cron.15sec > /dev/null 2> /dev/null
* * * * * root sleep 45; run-parts /etc/cron.15sec > /dev/null 2> /dev/null

* * * * * root run-parts /etc/cron.minute > /dev/null 2> /dev/null
*/5 * * * * root run-parts /etc/cron.5minute > /dev/null 2> /dev/null
+130

Won't running this in the background do it?

#!/bin/sh
while [ 1 ]; do
    echo "Hell yeah!" &
    sleep 15
done

This is about as efficient as it gets. The important part only gets executed every 15 seconds and the script sleeps the rest of the time (thus not wasting cycles).

Edits must be at least 8 characters (which is idiotic, IMHO) so I couldn't add the & at the end of line 3. In any case, as is, this doesn't run every 15 seconds. This runs every "15 seconds + however long echo hello takes to run". Which could be 0.01 seconds; could be 19 hours. – Parthian Shot Aug 11 '15 at 17:25

+10

I wrote a scheduler faster than cron. I have also implemented an overlapping guard. You can configure the scheduler to not start new process if previous one is still running. Take a look at https://github.com/sioux1977/scheduler/wiki

0

Use nanosleep(2). It uses structure timespec that is used to specify intervals of time with nanosecond precision.

struct timespec {
           time_t tv_sec;        /* seconds */
           long   tv_nsec;       /* nanoseconds */
       };

I'm going to go ahead and guess they don't need nanosecond precision, since the thing they're spawning every 15 seconds is a shell script, not a kernel thread. – Parthian Shot Aug 10 '15 at 23:08

@ParthianShot maybe but you never know. – Alexander Suraphel Aug 11 '15 at 9:23

0
#! /bin/sh

# Run all programs in a directory in parallel
# Usage: run-parallel directory delay
# Copyright 2013 by Marc Perkel
# docs at http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron"
# Free to use with attribution

if [ $# -eq 0 ]
then
   echo
   echo "run-parallel by Marc Perkel"
   echo
   echo "This program is used to run all programs in a directory in parallel" 
   echo "or to rerun them every X seconds for one minute."
   echo "Think of this program as cron with seconds resolution."
   echo
   echo "Usage: run-parallel [directory] [delay]"
   echo
   echo "Examples:"
   echo "   run-parallel /etc/cron.20sec 20"
   echo "   run-parallel 20"
   echo "   # Runs all executable files in /etc/cron.20sec every 20 seconds or 3 times a minute."
   echo 
   echo "If delay parameter is missing it runs everything once and exits."
   echo "If only delay is passed then the directory /etc/cron.[delay]sec is assumed."
   echo
   echo 'if "cronsec" is passed then it runs all of these delays 2 3 4 5 6 10 12 15 20 30'
   echo "resulting in 30 20 15 12 10 6 5 4 3 2 executions per minute." 
   echo
   exit
fi

# If "cronsec" is passed as a parameter then run all the delays in parallel

if [ $1 = cronsec ]
then
   $0 2 &
   $0 3 &
   $0 4 &
   $0 5 &
   $0 6 &
   $0 10 &
   $0 12 &
   $0 15 &
   $0 20 &
   $0 30 &
   exit
fi

# Set the directory to first prameter and delay to second parameter

dir=$1
delay=$2

# If only parameter is 2,3,4,5,6,10,12,15,20,30 then automatically calculate 
# the standard directory name /etc/cron.[delay]sec

if [[ "$1" =~ ^(2|3|4|5|6|10|12|15|20|30)$ ]]
then
   dir="/etc/cron.$1sec"
   delay=$1
fi

# Exit if directory doesn't exist or has no files

if [ ! "$(ls -A $dir/)" ]
then
   exit
fi

# Sleep if both $delay and $counter are set

if [ ! -z $delay ] && [ ! -z $counter ]
then
   sleep $delay
fi

# Set counter to 0 if not set

if [ -z $counter ]
then
   counter=0
fi

# Run all the programs in the directory in parallel
# Use of timeout ensures that the processes are killed if they run too long

for program in $dir/* ; do
   if [ -x $program ] 
   then
      if [ "0$delay" -gt 1 ] 
      then
         timeout $delay $program &> /dev/null &
      else
         $program &> /dev/null &
      fi
   fi
done

# If delay not set then we're done

if [ -z $delay ]
then
   exit
fi

# Add delay to counter

counter=$(( $counter + $delay ))

# If minute is not up - call self recursively

if [ $counter -lt 60 ]
then
   . $0 $dir $delay &
fi

# Otherwise we're done
0

Since my previous answer I came up with another solution that is different and perhaps better. This code allows processes to be run more than 60 times a minute with microsecond precision. You need the usleep program to make this work. Should be good to up to 50 times a second.

#! /bin/sh

# Microsecond Cron
# Usage: cron-ms start
# Copyright 2014 by Marc Perkel
# docs at http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron"
# Free to use with attribution

basedir=/etc/cron-ms

if [ $# -eq 0 ]
then
   echo
   echo "cron-ms by Marc Perkel"
   echo
   echo "This program is used to run all programs in a directory in parallel every X times per minute."
   echo "Think of this program as cron with microseconds resolution."
   echo
   echo "Usage: cron-ms start"
   echo
   echo "The scheduling is done by creating directories with the number of"
   echo "executions per minute as part of the directory name."
   echo
   echo "Examples:"
   echo "  /etc/cron-ms/7      # Executes everything in that directory  7 times a minute"
   echo "  /etc/cron-ms/30     # Executes everything in that directory 30 times a minute"
   echo "  /etc/cron-ms/600    # Executes everything in that directory 10 times a second"
   echo "  /etc/cron-ms/2400   # Executes everything in that directory 40 times a second"
   echo
   exit
fi

# If "start" is passed as a parameter then run all the loops in parallel
# The number of the directory is the number of executions per minute
# Since cron isn't accurate we need to start at top of next minute

if [ $1 = start ]
then
   for dir in $basedir/* ; do
      $0 ${dir##*/} 60000000 &
   done
   exit
fi

# Loops per minute and the next interval are passed on the command line with each loop

loops=$1
next_interval=$2

# Sleeps until a specific part of a minute with microsecond resolution. 60000000 is full minute

usleep $(( $next_interval - 10#$(date +%S%N) / 1000 ))

# Run all the programs in the directory in parallel

for program in $basedir/$loops/* ; do
   if [ -x $program ] 
   then
      $program &> /dev/null &
   fi
done

# Calculate next_interval

next_interval=$(($next_interval % 60000000 + (60000000 / $loops) ))

# If minute is not up - call self recursively

if [ $next_interval -lt $(( 60000000 / $loops * $loops)) ]
then
   . $0 $loops $next_interval &
fi

# Otherwise we're done

編輯原始版本而不是再次發布! - Vogon Jeltz於2016年2月20日15:47

-20

To avoid possible overlapping of execution, use a locking mechanism as described in that thread.

-1 OP沒有說他需要避免重疊執行; 事情可能是可重入的。另外,這不回答這個問題。 - Parthian Shot 2015年8月11日17:26

這本來是一個很好的評論,我會贊成 - KiJéy179年11月29日15:30

173
votes
answers
29 views
+10

How to properly nest Bash backticks

Either I missed some backlash or backlashing does not seem to work with too much programmer-quote-looping.

$ echo "hello1-`echo hello2-`echo hello3-`echo hello4```"

hello1-hello2-hello3-echo hello4

Wanted

hello1-hello2-hello3-hello4-hello5-hello6-...
up vote 123 down vote accepted favorite
沙发
+1230
+50

Use $(commands) instead:

$ echo "hello1-$(echo hello2-$(echo hello3-$(echo hello4)))"
hello1-hello2-hello3-hello4

$(commands) does the same thing as backticks, but you can nest them.

You may also be interested in Bash range expansions:

echo hello{1..10}
hello1 hello2 hello3 hello4 hello5 hello6 hello7 hello8 hello9 hello10

像{1..10}一樣+1。用數組限制它?ZSH可以“$ {$(date)[2,4]}”。為什麼不:“echo $ {echo hello1 - $(echo hello2)[1]}”? - 嗯,2010年4月17日11:03

這不會為每個嵌套命令創建子shell嗎? - Jordan Mackie 18年7月4日9:49

+310

if you insist to use backticks, following could be done

$ echo "hello1-`echo hello2-`echo hello3-\`echo hello4\```"

you have to put backslashes, \ \\ \\\\ by 2x and so on, its just very ugly, use $(commands) as other suggested.

這實際上回答了問題 - Justas 12月23日在20:06

+100

Any time you want to evaluate a command use command substitution:

$(command)

Any time you want to evaluate an arithmetic expression use expression substitution:

$((expr))

You can nest these like this:

Let's say file1.txt is 30 lines long and file2.txt is 10 lines long, than you can evaluate an expression like this:

$(( $(wc -l file1.txt) - $(wc -l file2.txt) ))

which would output 20 ( the difference in number of lines between two files).

+90

It's a lot easier if you use bash's $(cmd) command substitution syntax, which is much more friendly to being nested:

$ echo "hello1-$(echo hello2-$(echo hello3-$(echo hello4)))"
hello1-hello2-hello3-hello4

這不僅限於bash。它適用於符合POSIX 1003.1(“POSIX shell”)和大多數Bourne派生shell(ksh,ash,dash,bash,zsh等)的所有shell,但不是實際的Bourne shell(即heirloom.sourceforge.net) /sh.html)。 - 克里斯約翰森2010年4月17日3:02

158
votes
answers
22 views
+10

File content into unix variable with newlines

I have a text file test.txt with the following content:

text1
text2 

And I want to assign the content of the file to a UNIX variable, but when I do this:

testvar=$(cat test.txt)
echo $testvar

the result is:

text1 text2

instead of

text1
text2 

Can someone suggest me a solution for this?

up vote 139 down vote accepted favorite
沙发
+1390
+50

The assignment does not remove the newline characters, it's actually the echo doing this. You need simply put quotes around the string to maintain those newlines:

echo "$testvar"

This wil give the result you want. See the following transcript for a demo:

pax> cat num1.txt ; x=$(cat num1.txt)
line 1
line 2

pax> echo $x ; echo '===' ; echo "$x"
line 1 line 2
===
line 1
line 2

The reason why newlines are replaced with spaces is not entirely to do with the echo command, rather it's a combination of things.

When given a command line, bash splits it into words according to the documentation for the IFS variable:

IFS: The Internal Field Separator that is used for word splitting after expansion ... the default value is <space><tab><newline>.

That specifies that, by default, any of those three characters can be used to split your command into individual words. After that, the word separators are gone, all you have left is a list of words.

Combine that with the echo documentation (a bash internal command), and you'll see why the spaces are output:

echo [-neE] [arg ...]: Output the args, separated by spaces, followed by a newline.

When you use echo "$x", it forces the entire x variable to be a single word according to bash, hence it's not split. You can see that with:

pax> function count {
...>    echo $#
...> }
pax> count 1 2 3
3
pax> count a b c d
4
pax> count $x
4
pax> count "$x"
1

Here, the count function simply prints out the number of arguments given. The 1 2 3 and a b c d variants show it in action.

Then we try it with the two variations on the x variable. The one without quotes shows that there are four words, "test", "1", "test" and "2". Adding the quotes makes it one single word "test 1 test 2".

引號......這就是我所遺漏的!謝謝! - 1月24日2:33匿名

+80

Bash -ge 4 has the mapfile builtin to read lines from the standard input into an array variable.

help mapfile 

mapfile < file.txt lines
printf "%s" "${lines[@]}"

mapfile -t < file.txt lines    # strip trailing newlines
printf "%s
" "${lines[@]}" 

See also:

http://bash-hackers.org/wiki/doku.php/commands/builtin/mapfile

+80

This is due to IFS (Internal Field Separator) variable which contains newline.

$ cat xx1
1
2

$ A=`cat xx1`
$ echo $A
1 2

$ echo "|$IFS|"
|       
|

A workaround is to reset IFS to not contain the newline, temporarily:

$ IFSBAK=$IFS
$ IFS=" "
$ A=`cat xx1` # Can use $() as well
$ echo $A
1
2
$ IFS=$IFSBAK

To REVERT this horrible change for IFS:

IFS=$IFSBAK

怎麼重置這個? - Pooja25 2014年3月12日9:45

我做了這個改變,但現在我的其他東西都沒有用,請告訴我重置它? - Pooja25 2014年3月12日9:48

+20

Your variable is set correctly by testvar=$(cat test.txt). To display this variable which consist new line characters, simply add double quotes, e.g.

echo "$testvar" 

Here is the full example:

$ printf "test1
test2" > test.txt
$ testvar=$(<test.txt)
$ grep testvar <(set)
testvar=$'test1
test2'
$ echo "$testvar"
text1
text2
$ printf "%b" "$testvar"
text1
text2
+10

Just if someone is interested in another option:

content=( $(cat test.txt) )

a=0
while [ $a -le ${#content[@]} ]
do
        echo ${content[$a]}
        a=$[a+1]
done
0

The envdir utility provides an easy way to do this. envdir uses files to represent environment variables, with file names mapping to env var names, and file contents mapping to env var values. If the file contents contain newlines, so will the env var.

See https://pypi.python.org/pypi/envdir