stdout stderr 重定向到文件

本文详细介绍了C/C++中stdout、stderr的重定向方法,包括dup/dup2、freopen以及命令行重定向,并通过实例展示了如何使用这些技术将标准输出和错误重定向到文件。此外,还涵盖了freopen函数的用法,方便在程序中实现输入输出的灵活切换。

1. stdout/stderr 重定向

1.1. dup/dup2 重定向到已打开文件 或 新文件

// https://www.man7.org/linux/man-pages/man2/dup.2.html
#include <unistd.h>
// 给 oldfd 分配一个新的描述符,返回
int dup(int oldfd);
// 给 oldfd 分配一个新的描述符 newfd,如果 newfd 已经打开,则先关闭
// 原来只有 oldfd 一个指针,现在有2个指针执行那个句柄对应的文件啦
int dup2(int oldfd, int newfd);
#include <stdio.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int mOldStdout = -1;
int mNewStdout = -1;

// stdout 输出 到文件中
void dupOutput(char * filename)
{
    mNewStdout = open(filename, O_CREAT|O_WRONLY|O_APPEND|O_SYNC, S_IRWXU|S_IRWXG|S_IROTH);
    mOldStdout = dup(STDOUT_FILENO);

    fsync(STDOUT_FILENO);
    dup2(mNewStdout, STDOUT_FILENO); // STDOUT_FILENO 指向 mNewStdout 文件,stdout 输出就会写到文件里
}

// 还原 stdout 到 命令窗口
void restoreOutput()
{
    fsync(STDOUT_FILENO);

    dup2(mOldStdout, STDOUT_FILENO);
    close(mOldStdout);
    close(mNewStdout);
}

int main(int argc, char * argv[]) {
    printf("IN STDOUT-1\n");

    dupOutput("logfile");
    printf("IN LOGFILE-1\n");

    restoreOutput();
    printf("IN STDOUT-2\n");
}
#include <stdio.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

static FILE *logHandel = NULL;

int src_stdout = -1; // 一般 stdout 的 handel 是 1. stderr 是 2
int src_stderr = -1; // 一般 stdout 的 handel 是 1. stderr 是 2

void dupOutput(int fd)
{
    src_stdout = dup(STDOUT_FILENO);   // 保持老的 handle,用于恢复
    src_stderr = dup(STDERR_FILENO);
    fsync(STDOUT_FILENO);
    fsync(STDERR_FILENO);
    dup2(fd, STDOUT_FILENO);  // 把 STDOUT_FILENO 指向 fd 句柄
    dup2(fd, STDERR_FILENO);
}

void restoreOutput(int fd)
{
    fsync(STDOUT_FILENO);
    fsync(STDERR_FILENO);
    dup2(src_stdout, STDOUT_FILENO);  // STDOUT_FILENO 指向原来的 handle
    dup2(src_stderr, STDERR_FILENO);
    close(src_stdout);
    close(src_stderr);
}

int log_set_path(const char *path)
{
    if (access(path, F_OK | W_OK) == 0) {
        logHandel = fopen(path, "a+");
    }
    if (!logHandel) {
        printf("open log file(%s) error: %s\n", path, strerror(errno));
        return errno;
    }
    dupOutput(fileno(logHandel));
    return 0;
}

1.2. freopen 重定向到新文件

FILE *freopen(char *filename, char *type, FILE *stream);

freopen 类似于 fopen+dup2, 同时包含打开和重定向功能

#include <stdio.h>
int main() 
{ 
	int a,b; 
	freopen("D:\\in.txt","r",stdin); //输入重定向,输入数据将从D盘根目录下的in.txt文件中读取 
	freopen("D:\\out.txt","w",stdout); //输出重定向,输出数据将保存在D盘根目录下的out.txt文件中 
	while(scanf("%d %d",&a,&b)!=EOF) 
	printf("%d\n",a+b); 
	fclose(stdin);//关闭重定向输入 
	fclose(stdout);//关闭重定向输出 
	return 0; 
}

#include <stdio.h>
#include <iostream>
int main()
{ 
	int a,b; 
	freopen("D:\\in.txt","r",stdin); //输入重定向,输入数据将从D盘根目录下的in.txt文件中读取 
	freopen("D:\\out.txt","w",stdout); //输出重定向,输出数据将保存在D盘根目录下的out.txt文件中 
	while(cin>>a>>b) 
	cout<<a+b<<endl; // 注意使用endl 
	fclose(stdin);//关闭重定向输入
	fclose(stdout);//关闭重定向输出 
	return 0; 
}

1.3. 命令行重定向

这里主要是重定向符号的使用

新建文件 > 追加文件 >>
nohup java -jar app.jar >log 2>&1 &
简写 nohup java -jar app.jar &>log &

1.4. 参考资料

<think>我们正在讨论如何将Linux服务的stderr重定向文件。根据引用[2]引用[4],我们可以使用systemd的服务单元文件配置来实现。主要方法是在服务单元文件中使用`StandardError`指令或者通过`ExecStart`命令行的重定向。有两种主要方法:方法1:在[Service]部分使用`StandardError`指令(推荐)方法2:在ExecStart命令中使用shell重定向(更灵活,但需要启动shell)下面详细介绍:###方法1:使用systemd的`StandardError`指令(推荐)这是systemd原生的方式,不需要启动额外的shell进程。1.编辑服务单元文件(例如:`/etc/systemd/system/your-service.service`)2.在`[Service]`部分添加:```ini[Service]...StandardError=file:/path/to/your/error.log```或者,如果你希望同时重定向stdoutstderr,可以使用:```ini[Service]...StandardOutput=file:/path/to/your/output.logStandardError=file:/path/to/your/error.log```也可以将stderr合并到stdout,然后将两者重定向到同一个文件:```ini[Service]...StandardOutput=file:/path/to/your/combined.logStandardError=inherit#将stderr重定向到与stdout相同的文件```3.重新加载systemd配置并重启服务:```bashsudosystemctldaemon-reloadsudosystemctlrestartyour-service```###方法2:在`ExecStart`中使用shell重定向(适用于复杂重定向)如果需要在重定向的同时进行其他操作(如添加时间戳),可以使用此方法。1.修改服务单元文件,在`ExecStart`中使用shell命令来重定向:```ini[Service]...ExecStart=/bin/sh-c'/path/to/your/service-binary2>/path/to/your/error.log'```或者同时重定向stdoutstderr:```iniExecStart=/bin/sh-c'/path/to/your/service-binary>/path/to/output.log2>/path/to/error.log'```或者将stderr合并到stdout,然后重定向到一个文件:```iniExecStart=/bin/sh-c'/path/to/your/service-binary>/path/to/combined.log2>&1'```2.注意:如果需要在重定向前添加时间戳,可以结合管道(如引用[3]所示):```iniExecStart=/bin/sh-c'/path/to/your/service-binary2>&1|awk\"{printstrftime(\\\"%Y-%m-%d%H:%M:%S:\\\"),\\$0;fflush();}\">/path/to/error.log'```这个例子将stderr重定向stdout,然后通过管道传给awk命令添加时间戳,最后写入文件。###其他注意事项1.**文件权限**:确保服务运行的用户有权限写入目标文件。2.**日志轮转**:对于长期运行的服务,建议配置日志轮转(logrotate)或使用systemd的日志管理功能。3.**系统日志**:如果同时希望保留journald日志,可以设置`StandardError`为`syslog`(默认行为),然后通过`journalctl`查看。###验证重定向是否生效-检查服务状态:`systemctlstatusyour-service`-查看目标文件:`tail-f/path/to/your/error.log`-使用journalctl查看:`journalctl-uyour-service`(注意:如果重定向文件,则journald可能不再捕获这些输出)###参考示例(来自引用[2])引用[2]中给出了一个在ExecStart中使用重定向的例子:```iniExecStart=/bin/sh-c'/usr/local/bin/binary1agent-config-dir/etc/sample.d/server2>&1>/var/log.log'```这个例子将stdoutstderr重定向到了`/var/log.log`(注意:这个命令实际上是先合并stderrstdout,然后重定向文件,但写法上2>&1在前,>在后,这是正确的顺序)。###相关问题§§相关问题§§1.如何将Linux服务的stdoutstderr同时重定向到同一个文件?2.如何在重定向服务输出时添加时间戳?3.如何配置systemd服务的日志轮转?4.如何查看systemd服务的实时日志?</think>### Linux服务stderr重定向文件的方法 在Linux系统中,将服务的标准错误输出(stderr)重定向文件主要有以下几种方法: #### 方法1:在systemd服务文件中直接配置(推荐) ```ini [Service] # 仅重定向stderr文件 StandardError=file:/var/log/service-errors.log # 同时重定向stdoutstderr到不同文件 StandardOutput=file:/var/log/service-output.log StandardError=file:/var/log/service-errors.log # 将stderr合并到stdout重定向到同一文件 StandardOutput=file:/var/log/service-combined.log StandardError=inherit ``` **操作步骤:** 1. 编辑服务配置文件: ```bash sudo nano /etc/systemd/system/your-service.service ``` 2. 添加上述重定向配置 3. 重新加载配置并重启服务: ```bash sudo systemctl daemon-reload sudo systemctl restart your-service ``` #### 方法2:在ExecStart中使用Shell重定向 ```ini [Service] ExecStart=/bin/sh -c '/path/to/your-service 2>/var/log/service-errors.log' # 同时重定向stdoutstderr ExecStart=/bin/sh -c '/path/to/your-service >/var/log/output.log 2>/var/log/errors.log' # 合并输出到单个文件 ExecStart=/bin/sh -c '/path/to/your-service &>/var/log/combined.log' ``` #### 方法3:使用syslog重定向(需要rsyslog配置) 1. 在服务文件中配置: ```ini [Service] StandardError=syslog SyslogIdentifier=your-service ``` 2. 创建rsyslog配置: ```bash # /etc/rsyslog.d/your-service.conf if $programname == 'your-service' then /var/log/service-errors.log & stop ``` 3. 重启服务: ```bash sudo systemctl restart rsyslog your-service ``` ### 验证与调试 1. **检查日志文件**: ```bash tail -f /var/log/service-errors.log ``` 2. **查看服务状态**: ```bash sudo systemctl status your-service ``` 3. **检查journal日志**: ```bash journalctl -u your-service -b --no-pager ``` ### 注意事项 1. **文件权限**:确保服务运行用户有写入权限 ```bash sudo touch /var/log/service-errors.log sudo chown service-user:service-group /var/log/service-errors.log ``` 2. **日志轮转**:配置logrotate管理日志文件 3. **实时监控**:使用`tail -f`实时查看日志更新 4. **错误类型**:`2>`仅重定向标准错误,`&>`重定向所有输出 > **最佳实践**:对于生产环境,推荐使用方法1的`StandardError`指令,这是systemd原生支持的最可靠方式[^2][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值