Image

在日常的服务器管理中,是否经常为忘记曾经使用过的命令而苦恼?本文将分享如何通过编写简单的脚本和前端页面,记录并搜索所有执行过的Linux命令,让你的工作效率倍增。

在日常运维中,随着服务器数量的增加和使用时间的延长,我们可能会忘记一些曾经使用过的有用命令。当需要再次使用时,却苦于找不到。为了解决这个问题,我决定开发一个网页工具,记录所有执行的命令,方便日后查询。

一、收集执行的命令

首先,我们需要在每次执行命令后,收集相关信息并发送到外部接口。我的思路是编写一个Shell脚本,在每个命令执行后异步调用。以下是脚本的内容:

1. 编写收集命令的脚本

创建文件 /root/send_command.sh,并确保它具有可执行权限:

chmod +x /root/send_command.sh

脚本内容如下:

#!/bin/bash

# 获取传递的命令
COMMAND="$1"

# 获取主机名
HOSTNAME=$(hostname)

# 获取IP地址(取第一个非回环地址)
IP_ADDRESS=$(hostname -I | awk '{print $1}')

# 获取当前用户
USER_NAME=$(whoami)

# 获取执行时间(ISO 8601 格式)
EXECUTION_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

# 获取操作系统版本
OS_VERSION=$(grep -E '^PRETTY_NAME=' /etc/os-release | cut -d '"' -f 2)

# 构建JSON数据
JSON_DATA=$(cat <<EOF
{
  "command": "$COMMAND",
  "hostname": "$HOSTNAME",
  "ip_address": "$IP_ADDRESS",
  "user": "$USER_NAME",
  "execution_time": "$EXECUTION_TIME",
  "os_version": "$OS_VERSION"
}
EOF
)

# 发送数据到API
curl -X POST -H "Content-Type: application/json" \
     -d "$JSON_DATA" \
     https://example.com/send_command >/dev/null 2>&1

exit 0

2. 修改 .bashrc 文件

.bashrc 文件末尾添加以下内容,以确保每次命令执行后都会调用上述脚本:

# 定义脚本路径
SEND_COMMAND_SCRIPT="/root/send_command.sh"

# 定义函数,获取最近执行的命令并触发脚本
function send_last_command() {
    # 获取最近一条命令
    local CMD=$(history 1 | sed 's/^[ ]*[0-9]\+[ ]*//')

    # 排除特定命令,避免递归调用
    case "$CMD" in
        send_command.sh|another_excluded_command)
            ;;
        *)
            # 异步调用脚本
            nohup "$SEND_COMMAND_SCRIPT" "$CMD" >/dev/null 2>&1 &
            disown
            ;;
    esac
}

# 设置PROMPT_COMMAND,每次命令执行后触发函数
PROMPT_COMMAND="send_last_command;$PROMPT_COMMAND"

通过上述步骤,所有执行的命令都会被发送到指定的API接口。

二、展示和搜索命令

为了方便地查询和管理收集到的命令信息,我选择使用 Parse 作为后端服务,并开发了一个简单的前端页面来展示和搜索命令。

1. 前端页面代码

以下是完整的前端页面代码:

<!DOCTYPE html>
<html>
<head>
  <title>Linux 命令历史搜索</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css">
  <script src="https://unpkg.com/vue@3"></script>
  <script src="https://unpkg.com/parse/dist/parse.min.js"></script>
</head>
<body>
  <div id="app" class="ui container" style="margin-top: 20px;">
    <h2 class="ui header">Linux 命令历史搜索</h2>

    <div class="ui fluid icon input">
      <input type="text" v-model="searchQuery" placeholder="搜索命令、主机名、用户...">
      <i class="search icon"></i>
    </div>

    <div class="ui fluid icon input" style="margin-top: 10px;">
      <input type="text" v-model="excludeCommands" placeholder="输入要排除的命令 (例如:ls, echo)">
      <i class="filter icon"></i>
    </div>

    <table class="ui celled table" style="margin-top: 20px;">
      <thead>
        <tr>
          <th>命令</th>
          <th>主机名</th>
          <th>IP 地址</th>
          <th>用户</th>
          <th>执行时间</th>
          <th>操作系统版本</th>
          <th>备注</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="command in filteredCommands" :key="command.id">
          <td>{{ command.get('command') }}</td>
          <td>{{ command.get('hostname') }}</td>
          <td>{{ command.get('ip_address') }}</td>
          <td>{{ command.get('user') }}</td>
          <td>{{ formatBeijingTime(command.get('execution_time')) }}</td>
          <td>{{ command.get('os_version') }}</td>
          <td>
            <div class="ui action input">
              <input type="text" v-model="command.updatedRemark" :placeholder="command.get('remark') || '输入备注...'">
              <button class="ui primary button" @click="saveRemark(command)">保存</button>
            </div>
          </td>
        </tr>
      </tbody>
    </table>
  </div>

  <script type="module">
    const { createApp, ref, computed } = Vue;

    // 初始化 Parse
    Parse.initialize("your-app-id");
    Parse.serverURL = 'https://your-parse-server.com/parse';

    const LinuxCommand = Parse.Object.extend("LinuxCommand");

    createApp({
      setup() {
        const searchQuery = ref('');
        const excludeCommands = ref('ls, echo, pwd'); // 默认排除常用命令
        const commands = ref([]);

        // 获取命令数据
        async function fetchCommands() {
          const query = new Parse.Query(LinuxCommand);
          query.descending("execution_time");
          commands.value = await query.find();
          commands.value.forEach(command => {
            command.updatedRemark = command.get('remark') || '';
          });
        }

        // 格式化时间为北京时间
        function formatBeijingTime(utcTime) {
          const date = new Date(utcTime);
          return date.toLocaleString('zh-CN', { hour12: false });
        }

        // 保存备注信息
        async function saveRemark(command) {
          command.set('remark', command.updatedRemark);
          await command.save();
          fetchCommands();
        }

        // 过滤命令
        const filteredCommands = computed(() => {
          let filtered = commands.value;

          if (searchQuery.value) {
            const query = searchQuery.value.toLowerCase();
            filtered = filtered.filter(command => {
              return (
                command.get('command').toLowerCase().includes(query) ||
                command.get('hostname').toLowerCase().includes(query) ||
                command.get('ip_address').toLowerCase().includes(query) ||
                command.get('user').toLowerCase().includes(query) ||
                (command.get('remark') && command.get('remark').toLowerCase().includes(query))
              );
            });
          }

          if (excludeCommands.value) {
            const excludes = excludeCommands.value.toLowerCase().split(',').map(cmd => cmd.trim());
            filtered = filtered.filter(command => {
              return !excludes.includes(command.get('command').toLowerCase());
            });
          }

          return filtered;
        });

        // 初始获取数据
        fetchCommands();

        return {
          searchQuery,
          excludeCommands,
          commands,
          filteredCommands,
          formatBeijingTime,
          saveRemark
        };
      }
    }).mount('#app');
  </script>
</body>
</html>

2. 代码说明

  • 前端框架:使用了 Vue 3 和 Semantic UI,简单易用,界面美观。
  • 数据交互:通过 Parse SDK 与后端进行数据交互,获取并展示命令信息。
  • 功能特点

    • 搜索:可以根据命令、主机名、用户等关键词进行搜索。
    • 排除命令:支持输入要排除的命令,避免常用命令干扰搜索结果。
    • 备注:可以为每条命令添加备注,方便日后参考。

三、总结

通过以上步骤,我们成功地创建了一个能够记录并搜索所有执行过的Linux命令的工具。它不仅解决了遗忘命令的困扰,还提高了服务器管理的效率。后续我会继续完善这个工具,欢迎大家提出建议和意见。