看完这篇文章,对xargs不再似懂非懂

183 阅读2分钟

大家好,我是春哥,一名拥有10多年Linux后端研发经验的BAT互联网老兵。

1.概述

刚开始学习Linux时,对xargs命令总觉得似懂非懂,我相信很多人应该和春哥有类似的经历。今天就编写一个示例程序来彻底把xargs命令讲清楚。

2.xargs的作用

xargs命令的功能是:「把标准输入分割(换行、tab、空白符作为分隔符)后,转换成命令行参数,作为紧跟在其后的命令的命令行参数」,读起来是不是有点绕。没关系,下面我们自己来实现一个简单的xargs命令。

3.myxargs

还是贯彻春哥一贯的理念,要想理解就必须自己亲手创造一遍,春哥这里实现了一个非常简单myxargs命令,对应的代码内容如下。

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include <iostream>
#include <string>
#include <vector>

using namespace std;

string getCommand(const vector<string> &cmd) {
  string command = "";
  for (int i = 0; i < cmd.size(); i++) {
    command = command + cmd[i] + " ";
  }
  return command;
}

void execCmd(vector<string> &cmd) {
  string file = cmd[0];
  pid_t pid = fork();
  if (pid < 0) {
    perror("fork failed.");
    return;
  }
  if (pid == 0) {  // 子进程,使用子进程执行外部命令
    // 调用execl使用bash来执行单独的子命令,第一个参数是bash程序的绝对路径,相当于执行"bash -c command"
    // execl执行之后就陷入了bash命令中,bash命令执行失败时才会返回
    execl("/bin/bash", "bash", "-c", getCommand(cmd).c_str(), nullptr);
    exit(1);  // bash命令执行失败,则直接调用exit退出
  }
  int status = 0;
  int ret = waitpid(pid, &status, 0);  // 父进程调用waitpid等待子进程执行子命令结束,并获取子命令的执行结果
  if (ret != pid) {
    perror("waitpid failed.");
  }
}

int main(int argc, char *argv[]) {
  if (argc != 2) {
    cout << "invalid arguments." << endl;
    return -1;
  }
  string line;
  string argument;
  vector<string> cmd{argv[1]};
  while (cin >> line) {
    auto add_one_arg = [&cmd](string &arg) {
      if (arg != "") {
        cmd.push_back(arg);
      }
      arg = "";
    };
    for (size_t i = 0; i < line.size(); i++) {
      if (isblank(line[i])) {
        add_one_arg(argument);
        continue;
      }
      argument += line[i];
    }
    add_one_arg(argument);
  }
  execCmd(cmd);
  return 0;
}

myxargs的功能非常简单,就是从标准输入读取数据,边读数据边分割,直到数据从标准输入读完数据为止,然后再调用fork创建子进程,在子进程中调用execl函数来执行跟随在myxargs后面的命令(实际上是myargs命令的参数),并附带上转换得到的命令行参数。

我们来看一下myxargs的执行效果,并和xargs做个对比,相关的命令和输出如下所示。

// 执行g++命令来编译myxargs命令,注意这里要指定c++11的编译选项
root@VM-0-16-centos ~ $ g++ -std=c++11 -o myxargs myxargs.cpp
root@VM-0-16-centos ~ $ ls *.c | xargs wc
  65  198 1829 daemon.c
   8   21  144 helloworld.c
  73  219 1973 总用量
root@VM-0-16-centos ~ $ ls *.c | ./myxargs wc
  65  198 1829 daemon.c
   8   21  144 helloworld.c
  73  219 1973 总用量
root@VM-0-16-centos ~ $

从上面的输出可以看到,我们的myxargs和xargs的执行得到了同样的结果。

4.xargs和管道的区别

  • 管道是把上一个命令的标准输出转换成下一个命令的标准输入,在shell中用于连接多个命令的执行。
  • xargs则是把标准输入转换成命令行参数,传递给后面的命令。

xargs基本上都是和管道配合着使用。

5.写在最后

今天分享的内容就到这里,『如果本文对你有所帮助,记得关注我,我将持续在掘金上分享技术干货,关注我,下期分享不迷路』。

码字不易,跪求一赞!!!

推荐阅读:万字长文!带你吃透Reactor并发模型