71671


毎週日曜日の22時になったらGoスクリプトを実行するcronをEC2上に設定しました。

時刻になるとこのようなエラーが出て処理が止まってしまうのですが、原因がわかりません。

後述しますが、cronで設定したコマンドを直打ちすると動作するのに対し、cronとして設定すると動作しない点に悩んでおります。

[code]

    Failed to read CSV file open sample.csv: no such file or directory
    ※ csvファイルをmain.goから読み込めなかった場合に、コンソールにこのエラーが出力されるようにしています
    
[/code]

以下が詳細な状況になります。知見ある方がいらっしゃいましたらご助言頂きたいです。

よろしくお願いいたします。

## 状況

  * `crontab -e` でcronを登録
  * cronが実行できていることは確認済み
  * 実行後、`/var/spool/mail/ec2-user` に上記のエラーが書き込み
  * cronで指定したコマンドを直打ちした場合は問題なく動作する

### 実行結果

[code]

    # cronの確認
    [ec2-user@ip-xxx]$ crontab -l => 00 13 * * SUN /usr/bin/go run ~/go/src/project_name/main.go
    
    # crondの確認
    [ec2-user@ip-xxx]$ service crond status => Active: active (running) since xxxx Main PID xxxx(crond)
    
    # cron の実行の確認
    22時前
    [ec2-user@ip-xxx]$ sudo less /var/log/cron | grep main => 何も表示されず
    22時
    [ec2-user@ip-xxx]$ sudo less /var/log/cron | grep main => Nov 1 13:00:01 ip-xx-x-x-xxx CROND[xxxxx]: (ec2-user) CMD (/usr/bin/go run ~/go/src/project_name/main.go)
    
    # cron の出力を確認
    [ec2-user@ip-xxx]$ sudo less /var/spool/mail/ec2-user => Failed to read CSV file open sample.csv: no such file or directory
    
    # cron で指定したコマンドを直打ち
    [ec2-user@ip-xxx]$ /usr/bin/go run ~/go/src/project_name/main.go => 期待通りの出力
    
[/code]

## 試したこと

  * `~/go/src/project_name/main.go` の代わりに `~/go/src/project_name` を実行したが挙動は変わらず
  * `os.Open("sample.csv")` を `os.Open("~/go/src/project_name/sample.csv")` に変更しても変わらず

## 実行対象のGoファイル

[code]

    func main() {
        err, names, codes := readCSV("sample.csv")
        if err != nil {
            log.Fatal(err)
        }
    
        err, token, secret, id, _ := loadEnv()
        if err != nil {
            log.Fatal(err)
        }
    
        db, err := sql.Open("mysql", "root:root@/db_name")
        if err != nil {
            log.Fatal(err)
        }
        defer db.Close()
    
        // 上記のデータを使って処理開始
    }
    
    
    // csv読み込み
    func readCSV(fileName string) (err error, names []string, codes []string) {
        f, err := os.Open(fileName)
        if err != nil {
            fmt.Fprintln(os.Stderr, "Failed to read CSV file", err)
            return err, nil, nil
        }
    
        r := csv.NewReader(f)
        for {
            record, err := r.Read()
            if err == io.EOF {
                break
            }
            if err != nil {
                fmt.Fprintln(os.Stderr, "Failed to read CSV record", err)
                return err, nil, nil
            }
    
            codes = append(codes, record[0])
            names = append(names, name+"テスト")
        }
        return nil, names, codes
    
    }
    
    // 環境変数読み込み
    func loadEnv() (err error, token string, secret string, id string, sheetId string) {
        err = godotenv.Load()
        if err != nil {
            fmt.Fprintln(os.Stderr, "Failed to load .env file", err)
            return err, "", "", "", ""
        }
        token = os.Getenv("token")
        secret = os.Getenv("secret")
        id = os.Getenv("id")
        sheetId = os.Getenv("sheetId")
    
        return nil, token, secret, id, sheetId
    }
    
[/code]

## ディレクトリ構成

project_name

  * main.go
  * sample.csv
  * schools 
    * school.go
  * .env

## 環境

  * EC2(無料枠) 
    * t2.micro
    * Amazon Linux 2 AMI
    * MySQLの設定完了(RDSは使わない)
    * GOPATHは`~/go`に設定


CC BY-SA 4.0
2020-11-01T13:49:34.583
71645
2020-11-02T10:36:07.670
2020-11-02T03:15:05.030
42226
42226
question
aws go amazon-ec2 cron
EC2に設定したcronでgoファイルを実行すると他ファイルを読み込めない


> `os.Open("sample.csv")` を `os.Open("~/go/src/project_name/sample.csv")`
> に変更しても変わらず

`~`(tilde,チルダ)に関しては以下の回答で詳しく述べられていますので参照して下さい。

[チルダ と $HOME の違いってなんでしょうか？](https://ja.stackoverflow.com/a/21068/16894)

`~` という文字はシェルにおいては特殊な意味を持つ場合がありますが、golang では `~`
という文字そのものでしかありませんので、置き換える必要があります。具体的には実ユーザ(この場合は
`ec2-user`)のホームディレクトリに相当しますので、環境変数 `HOME` を使います。

[code]

    os.Open(filepath.Join(os.Getenv("HOME"), "go/src/project_name/sample.csv")) 
    
[/code]

**crontab(5)**

> Several environment variables are set up automatically by the cron(8)
> daemon. SHELL is set to /bin/sh, and LOGNAME and HOME are set from the
> /etc/passwd line of the crontab's owner.

ところで、本題とは関係ありませんが、以下の事が少し気になっています。

  * `go build` で作成した実行形式ファイルではなく、`go run` でプログラムを実行している
  * CSV ファイルのパスをプログラムの中に直接記述している(`hard coding`)

`go build` で作成した実行形式ファイルを使うのであれば、`filepath.Join(os.Getenv("HOME"),
"go/src/project_name/sample.csv")` などとしなくても済むのですが。。。


CC BY-SA 4.0
2020-11-02T10:36:07.670
71671
2020-11-02T10:36:07.670
71645
answer